hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 07c4c8efb39a29d5accfdd79a0581930e697104e
parent 8164df021f0deedbbc245f3e484ee9d3e478745c
Author: Lorenz (xha) <me@xha.li>
Date:   Sat, 30 Mar 2024 07:45:31 +0100

for-each changes

implement for-each for the parser, ast and add the done type to
hare::types::

also change a lot of modules to make use of for-each.

breaking changes:

* "done" cannot be used as a name anymore
* io::EOF is now a done type alias, which means that most stuff
  using it can be converted to for-each a iterator loop.
* strings::next() and strings::prev() return done instead of void
* glob::next() returns done instead of void
* path::nextiter() returns done instead of void
* fs::next() returns done instead of void

Signed-off-by: Lorenz (xha) <me@xha.li>

Diffstat:
Mascii/string.ha | 8++------
Mascii/valid.ha | 6+++---
Mcmd/genoiddb/main.ha | 84++++++++++++++++++++++++++++++-------------------------------------------------
Mcmd/hare/arch.ha | 6+++---
Mcmd/hare/build.ha | 3+--
Mcmd/hare/build/gather.ha | 6++----
Mcmd/hare/build/platform.ha | 6+++---
Mcmd/hare/build/queue.ha | 17++++++++---------
Mcmd/hare/build/types.ha | 16++++++++--------
Mcmd/hare/build/util.ha | 61++++++++++++++++++++++++++++++-------------------------------
Mcmd/hare/cache.ha | 9+++------
Mcmd/hare/deps.ha | 12++++++------
Mcmd/hare/util.ha | 10+++++-----
Mcmd/hare/version.ha | 18+++++++-----------
Mcmd/haredoc/doc/color.ha | 4++--
Mcmd/haredoc/doc/html.ha | 74++++++++++++++++++++++++++++++++++----------------------------------------
Mcmd/haredoc/doc/resolve.ha | 49++++++++++++++++++++++++-------------------------
Mcmd/haredoc/doc/sort.ha | 31+++++++++++++++----------------
Mcmd/haredoc/doc/tty.ha | 32+++++++++++++++-----------------
Mcmd/haredoc/doc/types.ha | 16++++++++--------
Mcmd/haredoc/doc/util.ha | 13++-----------
Mcmd/haredoc/main.ha | 64++++++++++++++++++++++++++++++++--------------------------------
Mcmd/haredoc/util.ha | 14+++++++-------
Mcmd/parsechk/main.ha | 5+----
Mencoding/base64/base64.ha | 10+++++-----
Mfmt/iter.ha | 14++++++--------
Mfmt/print.ha | 2+-
Mfnmatch/+test.ha | 3+--
Mfnmatch/fnmatch.ha | 20++++++++++----------
Mfs/fs.ha | 2+-
Mfs/types.ha | 2+-
Mfs/util.ha | 50++++++++++++++++++--------------------------------
Mgetopt/getopts.ha | 79++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mglob/+test.ha | 24++++++++++++------------
Mglob/glob.ha | 24++++++++++--------------
Mhare/ast/expr.ha | 16++++++++++++++--
Mhare/ast/type.ha | 4++--
Mhare/lex/+test.ha | 2+-
Mhare/lex/lex.ha | 2+-
Mhare/lex/token.ha | 6++++--
Mhare/module/deps.ha | 29+++++++++++++----------------
Mhare/module/format.ha | 30+++++++++++++++++-------------
Mhare/module/srcs.ha | 44++++++++++++++++++++++++--------------------
Mhare/module/util.ha | 31++++++++++++++++---------------
Mhare/parse/+test/expr_test.ha | 12++++++++++--
Mhare/parse/decl.ha | 14+++++---------
Mhare/parse/doc/doc.ha | 27+++++++++------------------
Mhare/parse/expr.ha | 126++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mhare/parse/type.ha | 13+++++++------
Mhare/types/+test.ha | 7++++---
Mhare/types/builtins.ha | 36++++++++++++++++++++++--------------
Mhare/types/hash.ha | 8+++++---
Mhare/types/store.ha | 5+++++
Mhare/types/types.ha | 6+++---
Mhare/unparse/decl.ha | 8+++-----
Mhare/unparse/expr.ha | 178+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mhare/unparse/import.ha | 6+++---
Mhare/unparse/type.ha | 15++++++++-------
Mhare/unparse/unit.ha | 4++--
Mio/copy.ha | 13+++----------
Mio/drain.ha | 10+++-------
Mio/types.ha | 2+-
Mmime/database.ha | 12++++++------
Mmime/entries+test.ha | 16++++++++--------
Mmime/lookup.ha | 6++----
Mmime/parse.ha | 10++--------
Mmime/system.ha | 17++---------------
Mnet/ip/ip.ha | 15++++++++-------
Mnet/ip/test+test.ha | 26++++++++++++--------------
Mnet/uri/fmt.ha | 12+++---------
Mnet/uri/parse.ha | 67+++++++++++++++++++------------------------------------------------
Mos/+freebsd/dirfdfs.ha | 12++++++------
Mos/+linux/dirfdfs.ha | 25+++++++++++++------------
Mos/+openbsd/dirfdfs.ha | 13+++++++------
Mos/environ.ha | 4++--
Mos/exec/+freebsd/exec.ha | 24++++++++++++------------
Mos/exec/+linux/exec.ha | 24++++++++++++------------
Mos/exec/+openbsd/exec.ha | 24++++++++++++------------
Mos/exec/cmd.ha | 18++++--------------
Mpath/buffer.ha | 9+++++----
Mpath/ext_stack.ha | 8++++----
Mpath/iter.ha | 22+++++++++++-----------
Mpath/stack.ha | 5+++--
Mregex/+test.ha | 38+++++++-------------------------------
Mregex/regex.ha | 44+++++++++++++++++++++-----------------------
Mshlex/escape.ha | 19++-----------------
Mshlex/split.ha | 20+++++---------------
Mstrings/concat.ha | 9+++++----
Mstrings/contains.ha | 4++--
Mstrings/dup.ha | 8++++----
Mstrings/index.ha | 16++++++++--------
Mstrings/iter.ha | 20++++++++++----------
Mstrings/replace.ha | 4++--
Mstrings/runes.ha | 26++++++++++++--------------
Mstrings/sub.ha | 2+-
Mstrings/template/template.ha | 23++++++++---------------
Mstrings/tokenize.ha | 14+++++++-------
Mstrings/trim.ha | 24++++++------------------
Mtest/+test.ha | 41++++++++++++++++++++---------------------
Mtest/util+test.ha | 5++---
Mtime/chrono/leapsec.ha | 27++++-----------------------
Mtime/chrono/timescale.ha | 11+++++------
Mtime/chrono/timezone.ha | 4++--
Mtime/date/date.ha | 3+--
Mtime/date/daydate.ha | 38+++++++++-----------------------------
Mtime/date/format.ha | 13++-----------
Mtime/date/parithm.ha | 10++--------
Mtime/date/parse.ha | 34+++++++++++-----------------------
Mtime/date/period.ha | 22+++++++++++-----------
Mtime/date/reckon.ha | 4+---
Mtime/date/virtual.ha | 3+--
111 files changed, 1072 insertions(+), 1231 deletions(-)

diff --git a/ascii/string.ha b/ascii/string.ha @@ -19,9 +19,7 @@ export fn strlower(s: str) str = { // to fit the entire string. export fn strlower_buf(s: str, buf: []u8) str = { let it = strings::iter(s); - for (true) match (strings::next(&it)) { - case void => break; - case let r: rune => + for (let r => strings::next(&it)) { static append(buf, utf8::encoderune(tolower(r))...); }; return strings::fromutf8(buf)!; @@ -41,9 +39,7 @@ export fn strupper(s: str) str = { // to fit the entire string. export fn strupper_buf(s: str, buf: []u8) str = { let it = strings::iter(s); - for (true) match (strings::next(&it)) { - case void => break; - case let r: rune => + for (let r => strings::next(&it)) { static append(buf, utf8::encoderune(toupper(r))...); }; return strings::fromutf8(buf)!; diff --git a/ascii/valid.ha b/ascii/valid.ha @@ -8,9 +8,9 @@ export fn valid(c: rune) bool = c: u32 <= 0o177; // Returns true if all runes in a string are valid ASCII characters. export fn validstr(s: str) bool = { - const s = strings::toutf8(s); - for (let i = 0z; i < len(s); i += 1) { - if (s[i] >= 0o200) { + const bytes = strings::toutf8(s); + for (let byte .. bytes) { + if (byte >= 0o200) { return false; }; }; diff --git a/cmd/genoiddb/main.ha b/cmd/genoiddb/main.ha @@ -33,8 +33,8 @@ export fn main() void = { write_db(os::stdout, oids)!; fmt::println("\tnames = [")!; - for (let i = 0z; i < len(oids); i += 1) { - fmt::printfln("\t\t\"{}\",", oids[i].name)!; + for (let oid .. oids) { + fmt::printfln("\t\t\"{}\",", oid.name)!; }; fmt::println("\t],")!; fmt::println("};\n")!; @@ -53,20 +53,12 @@ fn parse_oids() []entry = { defer bufio::finish(&s); let oids: []entry = []; - for (true) { - const l = match (bufio::scan_line(&s)!) { - case io::EOF => - break; - case let s: const str => - yield s; - }; - - if (l == "" || strings::hasprefix(l, '#')) { + for (let line => bufio::scan_line(&s)!) { + if (line == "" || strings::hasprefix(line, '#')) { continue; }; - - const p = strings::split(l, " "); + const p = strings::split(line, " "); defer free(p); const name = p[0]; const val = p[len(p)-1]; @@ -82,9 +74,9 @@ fn parse_oids() []entry = { }; fn free_oids(oids: []entry) void = { - for (let i = 0z; i < len(oids); i += 1) { - free(oids[i].name); - free(oids[i].val); + for (let oid .. oids) { + free(oid.name); + free(oid.val); }; free(oids); @@ -96,8 +88,7 @@ fn write_db(h: io::handle, oids: []entry) (void | io::error) = { const maxcols = 12z; let idx = 0z; - for (let i = 0z; i < len(oids); i += 1) { - let e = &oids[i]; + for (let e .. oids) { e.idx = idx; let der = oidtoder(e.val); @@ -105,9 +96,9 @@ fn write_db(h: io::handle, oids: []entry) (void | io::error) = { insert(der[0], len(der): u8); defer free(der); - for (let j = 0z; j < len(der); j += 1) { + for (let byte .. der) { fmt::print(if (idx % maxcols == 0) "\n\t\t" else " ")?; - fmt::printf("0x{:.2x},", der[j])?; + fmt::printf("0x{:.2x},", byte)?; idx += 1; }; }; @@ -132,8 +123,7 @@ fn oidtoder(oid: str) []u8 = { der[0] = nums[0]: u8 * 40 + nums[1]: u8; let end = 1z; - for (let i = 2z; i < len(nums); i += 1) { - let n = nums[i]; + for (let n .. nums) { if (n == 0) { insert(der[end], 0u8); end = len(der); @@ -162,13 +152,8 @@ fn oidtou64s(oid: str) []u64 = { let nums = strings::tokenize(oid, "."); let intnums: []u64 = []; - for (true) { - match (strings::next_token(&nums)) { - case let s: str => - append(intnums, strconv::stou64(s)!); - case void => - break; - }; + for (let s => strings::next_token(&nums)) { + append(intnums, strconv::stou64(s)!); }; return intnums; @@ -178,31 +163,26 @@ fn write_varname(h: io::handle, name: str) (void | io::error) = { // assume that names are in ascii let i = strings::iter(name); let prevlow = false; - for (true) { - match (strings::next(&i)) { - case void => - break; - case let r: rune => - let r = if (r == '-') { + for (let r => strings::next(&i)) { + let r = if (r == '-') { + prevlow = false; + yield '_'; + } else if (ascii::isdigit(r)) { + prevlow = true; + yield r; + } else if (ascii::isupper(r)) { + if (prevlow) { + fmt::fprint(h, "_")?; prevlow = false; - yield '_'; - } else if (ascii::isdigit(r)) { - prevlow = true; - yield r; - } else if (ascii::isupper(r)) { - if (prevlow) { - fmt::fprint(h, "_")?; - prevlow = false; - }; - yield r; - } else if (ascii::islower(r)) { - prevlow = true; - yield ascii::toupper(r); - } else { - fmt::fatalf("Unexpected character in oid name: {}", r); }; - - fmt::fprint(h, r)?; + yield r; + } else if (ascii::islower(r)) { + prevlow = true; + yield ascii::toupper(r); + } else { + fmt::fatalf("Unexpected character in oid name: {}", r); }; + + fmt::fprint(h, r)?; }; }; diff --git a/cmd/hare/arch.ha b/cmd/hare/arch.ha @@ -47,9 +47,9 @@ fn set_arch_tags(tags: *[]str, a: *build::arch) void = { }; fn get_arch(name: str) (*build::arch | unknown_arch) = { - for (let i = 0z; i < len(arches); i += 1) { - if (arches[i].name == name) { - return &arches[i]; + for (let arch &.. arches) { + if (arch.name == name) { + return arch; }; }; return name: unknown_arch; diff --git a/cmd/hare/build.ha b/cmd/hare/build.ha @@ -53,8 +53,7 @@ fn build(name: str, cmd: *getopt::command) (void | error) = { ctx.mode = build::output::SILENT; }; - for (let i = 0z; i < len(cmd.opts); i += 1) { - let opt = cmd.opts[i]; + for (let opt .. cmd.opts) { switch (opt.0) { case 'a' => arch = get_arch(opt.1)?; diff --git a/cmd/hare/build/gather.ha b/cmd/hare/build/gather.ha @@ -54,10 +54,8 @@ fn gather_submodules( let n = 0z; let it = os::iter(path::string(buf))?; defer fs::finish(it); - for (true) match (module::next(it)?) { - case void => - break; - case let dir: fs::dirent => + + for (let dir => module::next(it)?) { path::push(buf, dir.name)?; defer path::pop(buf); append(mod, dir.name); diff --git a/cmd/hare/build/platform.ha b/cmd/hare/build/platform.ha @@ -37,9 +37,9 @@ const platforms: [_]platform = [ ]; export fn get_platform(name: str) (*platform | unknown_platform) = { - for (let i = 0z; i < len(platforms); i += 1) { - if (platforms[i].name == name) { - return &platforms[i]; + for (let platform &.. platforms) { + if (platform.name == name) { + return platform; }; }; return name: unknown_platform; diff --git a/cmd/hare/build/queue.ha b/cmd/hare/build/queue.ha @@ -31,8 +31,8 @@ type job = struct { export fn execute(ctx: *context) (str | error) = { let q: []*task = []; defer free(q); - defer for (let i = 0z; i < len(q); i += 1) { - free_task(q[i]); + defer for (let t .. q) { + free_task(t); }; // stage::TD (typedef files) are generated by the SSA stage (harec) @@ -77,9 +77,9 @@ fn task_cmp(a: const *opaque, b: const *opaque) int = { fn queue(ctx: *context, q: *[]*task, kind: stage, idx: size) *task = { // return already existing task to avoid creating duplicates - for (let i = 0z; i < len(q); i += 1) { - if (q[i].kind == kind && q[i].idx == idx) { - return q[i]; + for (let t .. *q) { + if (t.kind == kind && t.idx == idx) { + return t; }; }; let t = alloc(task { @@ -98,8 +98,7 @@ fn queue(ctx: *context, q: *[]*task, kind: stage, idx: size) *task = { append(queue(ctx, q, kind - 1, idx).rdeps, t); case stage::SSA => t.ndeps = len(ctx.mods[idx].deps); - for (let i = 0z; i < t.ndeps; i += 1) { - let dep_idx = ctx.mods[idx].deps[i].0; + for (let (dep_idx, _) .. ctx.mods[idx].deps) { append(queue(ctx, q, stage::SSA, dep_idx).rdeps, t); }; case stage::TD => abort(); @@ -173,9 +172,9 @@ fn run_task(ctx: *context, jobs: *[]job, t: *task) (bool | error) = { }; case output::VVERBOSE => fmt::error(ctx.cmds[t.kind])?; - for (let i = 0z; i < len(args); i += 1) { + for (let arg .. args) { fmt::error(" ")?; - shlex::quote(os::stderr, args[i])?; + shlex::quote(os::stderr, arg)?; }; fmt::errorln()?; }; diff --git a/cmd/hare/build/types.ha b/cmd/hare/build/types.ha @@ -37,8 +37,8 @@ export type task = struct { }; export fn free_task(t: *task) void = { - for (let i = 0z; i < len(t.rdeps); i += 1) { - t.rdeps[i].ndeps -= 1; + for (let rdep &.. t.rdeps) { + rdep.ndeps -= 1; }; free(t.rdeps); free(t); @@ -96,12 +96,12 @@ export type context = struct { export fn ctx_finish(ctx: *context) void = { free(ctx.ctx.tags); - for (let i = 0z; i < len(ctx.defines); i += 1) { - ast::ident_free(ctx.defines[i].ident); - ast::type_finish(ctx.defines[i]._type); - free(ctx.defines[i]._type); - ast::expr_finish(ctx.defines[i].init); - free(ctx.defines[i].init); + for (let define .. ctx.defines) { + ast::ident_free(define.ident); + ast::type_finish(define._type); + free(define._type); + ast::expr_finish(define.init); + free(define.init); }; free(ctx.defines); free(ctx.libdirs); diff --git a/cmd/hare/build/util.ha b/cmd/hare/build/util.ha @@ -48,8 +48,8 @@ fn get_deps(ctx: *context, t: *task) []str = { case stage::TD => abort(); case stage::SSA => let deps = strings::dupall(mod.srcs.ha); - for (let i = 0z; i < len(mod.deps); i += 1) { - append(deps, get_cache(ctx, mod.deps[i].0, stage::TD)!); + for (let (dep_idx, _) .. mod.deps) { + append(deps, get_cache(ctx, dep_idx, stage::TD)!); }; return deps; case stage::S => @@ -66,8 +66,8 @@ fn get_deps(ctx: *context, t: *task) []str = { append(deps, strings::dup(srcs.sc[j])); }; append(deps, get_cache(ctx, i, stage::O)!); - for (let j = 0z; j < len(srcs.o); j += 1) { - append(deps, strings::dup(srcs.o[j])); + for (let o .. srcs.o) { + append(deps, strings::dup(o)); }; }; return deps; @@ -109,9 +109,9 @@ fn get_flags(ctx: *context, t: *task) ([]str | error) = { case stage::O => return flags; case stage::BIN => - for (let i = 0z; i < len(ctx.libdirs); i += 1) { + for (let libdir .. ctx.libdirs) { append(flags, strings::dup("-L")); - append(flags, strings::dup(ctx.libdirs[i])); + append(flags, strings::dup(libdir)); }; if (ctx.libc) { append(flags, strings::dup("-Wl,--gc-sections")); @@ -154,23 +154,22 @@ fn get_flags(ctx: *context, t: *task) ([]str | error) = { append(flags, strings::dup("-T")); }; - for (let i = 0z; i < len(ctx.defines); i += 1) { - let ident = ctx.defines[i].ident; + for (let define .. ctx.defines) { + let ident = define.ident; let ns = ident[..len(ident) - 1]; if (!ast::ident_eq(ns, mod.ns)) { continue; }; let buf = memio::dynamic(); memio::concat(&buf, "-D", ident[len(ident) - 1])!; - match (ctx.defines[i]._type) { + match (define._type) { case null => void; case let t: *ast::_type => memio::concat(&buf, ":")!; unparse::_type(&buf, &unparse::syn_nowrap, t)!; }; memio::concat(&buf, "=")!; - unparse::expr(&buf, &unparse::syn_nowrap, - ctx.defines[i].init)!; + unparse::expr(&buf, &unparse::syn_nowrap, define.init)!; append(flags, memio::string(&buf)!); }; @@ -186,8 +185,8 @@ fn get_hash( let h = sha256::sha256(); hash::write(&h, strings::toutf8(ctx.cmds[t.kind])); - for (let i = 0z; i < len(flags); i += 1) { - hash::write(&h, strings::toutf8(flags[i])); + for (let flag .. flags) { + hash::write(&h, strings::toutf8(flag)); }; switch (t.kind) { @@ -197,8 +196,8 @@ fn get_hash( hash::write(&h, [0]); hash::write(&h, ctx.version); hash::write(&h, [0]); - for (let i = 0z; i < len(ctx.mods[t.idx].deps); i += 1) { - let ns = unparse::identstr(ctx.mods[t.idx].deps[i].1); + for (let dep .. ctx.mods[t.idx].deps) { + let ns = unparse::identstr(dep.1); defer free(ns); let var = strings::concat("HARE_TD_", ns); defer free(var); @@ -218,14 +217,14 @@ fn get_hash( hash::write(&h, [0]); case stage::O => void; case stage::BIN => - for (let i = 0z; i < len(ctx.libs); i += 1) { - hash::write(&h, strings::toutf8(ctx.libs[i])); + for (let lib .. ctx.libs) { + hash::write(&h, strings::toutf8(lib)); hash::write(&h, [0]); }; }; - for (let i = 0z; i < len(deps); i += 1) { - hash::write(&h, strings::toutf8(deps[i])); + for (let dep .. deps) { + hash::write(&h, strings::toutf8(dep)); hash::write(&h, [0]); }; @@ -259,13 +258,13 @@ fn get_args(ctx: *context, tmp: str, flags: []str, t: *task) []str = { case stage::BIN => for (let i = 0z; i < len(ctx.mods); i += 1) { let srcs = ctx.mods[i].srcs; - for (let i = 0z; i < len(srcs.sc); i += 1) { + for (let sc .. srcs.sc) { append(args, strings::dup("-T")); - append(args, strings::dup(srcs.sc[i])); + append(args, strings::dup(sc)); }; append(args, get_cache(ctx, i, stage::O)!); - for (let i = 0z; i < len(srcs.o); i += 1) { - append(args, strings::dup(srcs.o[i])); + for (let o .. srcs.o) { + append(args, strings::dup(o)); }; }; // XXX: when dynamically linking on Linux, we have to disable @@ -275,14 +274,14 @@ fn get_args(ctx: *context, tmp: str, flags: []str, t: *task) []str = { if (ctx.libc) { append(args, strings::dup("-Wl,--no-gc-sections")); }; - for (let i = 0z; i < len(ctx.libs); i += 1) { + for (let lib .. ctx.libs) { append(args, strings::dup("-l")); - append(args, strings::dup(ctx.libs[i])); + append(args, strings::dup(lib)); }; yield []; }; - for (let i = 0z; i < len(srcs); i += 1) { - append(args, strings::dup(srcs[i])); + for (let src .. srcs) { + append(args, strings::dup(src)); }; return args; }; @@ -291,8 +290,8 @@ fn write_args(ctx: *context, out: str, args: []str, t: *task) (void | error) = { let txt = os::create(out, 0o644)?; defer io::close(txt)!; if (t.kind == stage::SSA) { - for (let i = 0z; i < len(ctx.mods[t.idx].deps); i += 1) { - let ns = unparse::identstr(ctx.mods[t.idx].deps[i].1); + for (let (_, ident) .. ctx.mods[t.idx].deps) { + let ns = unparse::identstr(ident); defer free(ns); let var = strings::concat("HARE_TD_", ns); defer free(var); @@ -300,9 +299,9 @@ fn write_args(ctx: *context, out: str, args: []str, t: *task) (void | error) = { }; }; fmt::fprint(txt, ctx.cmds[t.kind])?; - for (let i = 0z; i < len(args); i += 1) { + for (let arg .. args) { fmt::fprint(txt, " ")?; - shlex::quote(txt, args[i])?; + shlex::quote(txt, arg)?; }; fmt::fprintln(txt)?; }; diff --git a/cmd/hare/cache.ha b/cmd/hare/cache.ha @@ -16,8 +16,7 @@ use path; fn cache(name: str, cmd: *getopt::command) (void | error) = { let clear = false; - for (let i = 0z; i < len(cmd.opts); i += 1) { - let opt = cmd.opts[i]; + for (let opt .. cmd.opts) { switch (opt.0) { case 'c' => clear = true; @@ -51,10 +50,8 @@ fn dirsize(buf: *path::buffer) (size | error) = { let s = 0z; let it = os::iter(path::string(buf))?; defer fs::finish(it); - for (true) match (fs::next(it)?) { - case void => - break; - case let d: fs::dirent => + + for (let d => fs::next(it)?) { path::push(buf, d.name)?; let stat = os::stat(path::string(buf))?; s += stat.sz; diff --git a/cmd/hare/deps.ha b/cmd/hare/deps.ha @@ -28,8 +28,8 @@ fn deps(name: str, cmd: *getopt::command) (void | error) = { let build_dir: str = ""; let goal = deps_fmt::TERM; - for (let i = 0z; i < len(cmd.opts); i += 1) { - let opt = cmd.opts[i]; + + for (let opt .. cmd.opts) { switch (opt.0) { case 'd' => goal = deps_fmt::DOT; @@ -70,11 +70,11 @@ fn deps(name: str, cmd: *getopt::command) (void | error) = { deps_graph(&mods); case deps_fmt::DOT => fmt::println("strict digraph deps {")!; - for (let i = 0z; i < len(mods); i += 1) { - for (let j = 0z; j < len(mods[i].deps); j += 1) { - const child = mods[mods[i].deps[j].0]; + for (let mod .. mods) { + for (let dep .. mod.deps) { + const child = mods[dep.0]; fmt::printfln("\t\"{}\" -> \"{}\";", - mods[i].name, child.name)!; + mod.name, child.name)!; }; }; fmt::println("}")!; diff --git a/cmd/hare/util.ha b/cmd/hare/util.ha @@ -15,17 +15,17 @@ fn merge_tags(current: *[]str, new: str) (void | module::error) = { }; let newtags = module::parse_tags(trimmed)?; defer free(newtags); - for :new (let i = 0z; i < len(newtags); i += 1) { + for :new (let newtag .. newtags) { for (let j = 0z; j < len(current); j += 1) { - if (newtags[i].name == current[j]) { - if (!newtags[i].include) { + if (newtag.name == current[j]) { + if (!newtag.include) { static delete(current[j]); }; continue :new; }; }; - if (newtags[i].include) { - append(current, newtags[i].name); + if (newtag.include) { + append(current, newtag.name); }; }; }; diff --git a/cmd/hare/version.ha b/cmd/hare/version.ha @@ -10,8 +10,7 @@ use strings; fn version(name: str, cmd: *getopt::command) (void | error) = { let verbose = false; - for (let i = 0z; i < len(cmd.opts); i += 1) { - const opt = cmd.opts[i]; + for (let opt .. cmd.opts) { switch (opt.0) { case 'v' => verbose = true; @@ -33,18 +32,15 @@ fn version(name: str, cmd: *getopt::command) (void | error) = { if (os::getenv("HAREPATH") is str) " (from environment)" else "")?; let tok = strings::tokenize(harepath(), ":"); - for (true) match (strings::next_token(&tok)) { - case void => - break; - case let s: str => + for (let s => strings::next_token(&tok)) { fmt::printfln("\t{}", s)?; }; fmt::println("toolchains:")?; - for (let i = 0z; i < len(arches); i += 1) { - fmt::printfln(" {}:", arches[i].name)?; - fmt::printfln("\tAS={}", arches[i].as_cmd)?; - fmt::printfln("\tCC={}", arches[i].cc_cmd)?; - fmt::printfln("\tLD={}", arches[i].ld_cmd)?; + for (let arch .. arches) { + fmt::printfln(" {}:", arch.name)?; + fmt::printfln("\tAS={}", arch.as_cmd)?; + fmt::printfln("\tCC={}", arch.cc_cmd)?; + fmt::printfln("\tLD={}", arch.ld_cmd)?; }; }; diff --git a/cmd/haredoc/doc/color.ha b/cmd/haredoc/doc/color.ha @@ -41,8 +41,8 @@ fn init_colors() (void | error) = { const matches = regex::findall(&expr, env_colors); defer regex::result_freeall(matches); - for (let i = 0z; i < len(matches); i += 1) :colors { - let (k, v) = (matches[i][1].content, matches[i][2].content); + for (let m .. matches) { + let (k, v) = (m[1].content, m[2].content); let idx = 0z; let out: *str = switch (k) { diff --git a/cmd/haredoc/doc/html.ha b/cmd/haredoc/doc/html.ha @@ -20,26 +20,21 @@ use strings; fn html_escape(out: io::handle, in: str) (size | io::error) = { let z = 0z; let iter = strings::iter(in); - for (true) { - match (strings::next(&iter)) { - case void => - break; - case let rn: rune => - z += fmt::fprint(out, switch (rn) { - case '&' => - yield "&amp;"; - case '<' => - yield "&lt;"; - case '>' => - yield "&gt;"; - case '"' => - yield "&quot;"; - case '\'' => - yield "&apos;"; - case => - yield strings::fromutf8(utf8::encoderune(rn))!; - })?; - }; + for (let rn => strings::next(&iter)) { + z += fmt::fprint(out, switch (rn) { + case '&' => + yield "&amp;"; + case '<' => + yield "&lt;"; + case '>' => + yield "&gt;"; + case '"' => + yield "&quot;"; + case '\'' => + yield "&apos;"; + case => + yield strings::fromutf8(utf8::encoderune(rn))!; + })?; }; return z; }; @@ -76,8 +71,8 @@ export fn emit_html(ctx: *context) (void | error) = { } else { fmt::fprintf(ctx.out, "<h2><span class='heading-body'>{}</span><span class='heading-extra'>", ident)?; }; - for (let i = 0z; i < len(ctx.tags); i += 1) { - fmt::fprintf(ctx.out, "+{} ", ctx.tags[i])?; + for (let tag .. ctx.tags) { + fmt::fprintf(ctx.out, "+{} ", tag)?; }; fmt::fprintln(ctx.out, "</span></h2>")?; @@ -103,8 +98,7 @@ export fn emit_html(ctx: *context) (void | error) = { fmt::fprintln(ctx.out, "<h3>Submodules</h3>")?; }; fmt::fprintln(ctx.out, "<ul class='submodules'>")?; - for (let i = 0z; i < len(ctx.submods); i += 1) { - let submodule = ctx.submods[i]; + for (let submodule .. ctx.submods) { let path = path::init("/", identpath, submodule)!; fmt::fprintf(ctx.out, "<li><a href='")?; @@ -133,36 +127,36 @@ export fn emit_html(ctx: *context) (void | error) = { if (len(decls.types) != 0) { fmt::fprintln(ctx.out, "<h3>Types</h3>")?; - for (let i = 0z; i < len(decls.types); i += 1) { - details(ctx, &decls.types[i])?; + for (let t &.. decls.types) { + details(ctx, t)?; }; }; if (len(decls.errors) != 0) { fmt::fprintln(ctx.out, "<h3>Errors</h3>")?; - for (let i = 0z; i < len(decls.errors); i += 1) { - details(ctx, &decls.errors[i])?; + for (let e &.. decls.errors) { + details(ctx, e)?; }; }; if (len(decls.constants) != 0) { fmt::fprintln(ctx.out, "<h3>Constants</h3>")?; - for (let i = 0z; i < len(decls.constants); i += 1) { - details(ctx, &decls.constants[i])?; + for (let c &.. decls.constants) { + details(ctx, c)?; }; }; if (len(decls.globals) != 0) { fmt::fprintln(ctx.out, "<h3>Globals</h3>")?; - for (let i = 0z; i < len(decls.globals); i += 1) { - details(ctx, &decls.globals[i])?; + for (let g &.. decls.globals) { + details(ctx, g)?; }; }; if (len(decls.funcs) != 0) { fmt::fprintln(ctx.out, "<h3>Functions</h3>")?; - for (let i = 0z; i < len(decls.funcs); i += 1) { - details(ctx, &decls.funcs[i])?; + for (let f &.. decls.funcs) { + details(ctx, f)?; }; }; }; @@ -286,8 +280,8 @@ fn htmlref(ctx: *context, ref: ast::ident) (void | error) = { }; fn html_paragraph(ctx: *context, p: doc::paragraph) (void | error) = { - for (let i = 0z; i < len(p); i += 1) { - match (p[i]) { + for (let elem .. p) { + match (elem) { case let s: str => match (uri::parse(s)) { case let uri: uri::uri => @@ -330,17 +324,17 @@ fn markup_html( }; defer doc::freeall(doc); - for (let i = 0z; i < len(doc); i += 1) { - match (doc[i]) { + for (let elem .. doc) { + match (elem) { case let p: doc::paragraph => fmt::fprint(ctx.out, "<p>")?; html_paragraph(ctx, p)?; fmt::fprintln(ctx.out)?; case let l: doc::list => fmt::fprintln(ctx.out, "<ul>")?; - for (let i = 0z; i < len(l); i += 1) { + for (let entry .. l) { fmt::fprint(ctx.out, "<li>")?; - html_paragraph(ctx, l[i])?; + html_paragraph(ctx, entry)?; fmt::fprintln(ctx.out)?; }; fmt::fprintln(ctx.out, "</ul>")?; diff --git a/cmd/haredoc/doc/resolve.ha b/cmd/haredoc/doc/resolve.ha @@ -71,32 +71,32 @@ fn is_local(ctx: *context, what: ast::ident) bool = { }; const summary = ctx.summary; - for (let i = 0z; i < len(summary.constants); i += 1) { - const name = decl_ident(&summary.constants[i])[0]; + for (let c &.. summary.constants) { + const name = decl_ident(c)[0]; if (name == what[0]) { return true; }; }; - for (let i = 0z; i < len(summary.errors); i += 1) { - const name = decl_ident(&summary.errors[i])[0]; + for (let e &.. summary.errors) { + const name = decl_ident(e)[0]; if (name == what[0]) { return true; }; }; - for (let i = 0z; i < len(summary.types); i += 1) { - const name = decl_ident(&summary.types[i])[0]; + for (let t &.. summary.types) { + const name = decl_ident(t)[0]; if (name == what[0]) { return true; }; }; - for (let i = 0z; i < len(summary.globals); i += 1) { - const name = decl_ident(&summary.globals[i])[0]; + for (let g &.. summary.globals) { + const name = decl_ident(g)[0]; if (name == what[0]) { return true; }; }; - for (let i = 0z; i < len(summary.funcs); i += 1) { - const name = decl_ident(&summary.funcs[i])[0]; + for (let f &.. summary.funcs) { + const name = decl_ident(f)[0]; if (name == what[0]) { return true; }; @@ -106,8 +106,7 @@ fn is_local(ctx: *context, what: ast::ident) bool = { }; fn lookup_local_enum(ctx: *context, what: ast::ident) (ast::ident | void) = { - for (let i = 0z; i < len(ctx.summary.types); i += 1) { - const decl = &ctx.summary.types[i]; + for (let decl &.. ctx.summary.types) { const name = decl_ident(decl)[0]; if (name == what[0]) { const t = (decl.decl as []ast::decl_type)[0]; @@ -117,8 +116,8 @@ fn lookup_local_enum(ctx: *context, what: ast::ident) (ast::ident | void) = { case => return; }; - for (let i = 0z; i < len(e.values); i += 1) { - if (e.values[i].name == what[1]) { + for (let value .. e.values) { + if (value.name == what[1]) { return what; }; }; @@ -143,34 +142,34 @@ fn lookup_remote_enum(ctx: *context, what: ast::ident) (ast::ident | void | erro // This would take a lot of memory to load let decls: []ast::decl = []; defer { - for (let i = 0z; i < len(decls); i += 1) { - ast::decl_finish(decls[i]); + for (let decl .. decls) { + ast::decl_finish(decl); }; free(decls); }; - for (let i = 0z; i < len(srcs.ha); i += 1) { - const in = srcs.ha[i]; + for (let in .. srcs.ha) { let u = scan(in)?; append(decls, u.decls...); }; - for (let i = 0z; i < len(decls); i += 1) { - const decl = match (decls[i].decl) { + for (let decl .. decls) { + const decls = match (decl.decl) { case let t: []ast::decl_type => yield t; case => continue; }; - for (let i = 0z; i < len(decl); i += 1) { - if (decl[i].ident[0] == decl_name) { - const e = match (decl[i]._type.repr) { + + for (let d .. decls) { + if (d.ident[0] == decl_name) { + const e = match (d._type.repr) { case let e: ast::enum_type => yield e; case => abort(); }; - for (let i = 0z; i < len(e.values); i += 1) { - if (e.values[i].name == member) { + for (let value .. e.values) { + if (value.name == member) { return what; }; }; diff --git a/cmd/haredoc/doc/sort.ha b/cmd/haredoc/doc/sort.ha @@ -17,8 +17,7 @@ use strings; export fn sort_decls(decls: []ast::decl) summary = { let sorted = summary { ... }; - for (let i = 0z; i < len(decls); i += 1) { - let decl = decls[i]; + for (let decl .. decls) { if (!decl.exported) { continue; }; @@ -38,42 +37,42 @@ export fn sort_decls(decls: []ast::decl) summary = { }, docs = decl.docs, }); - case let t: []ast::decl_type => - for (let j = 0z; j < len(t); j += 1) { + case let types: []ast::decl_type => + for (let t .. types) { let bucket = &sorted.types; - if (t[j]._type.flags & ast::type_flag::ERROR == ast::type_flag::ERROR) { + if (t._type.flags & ast::type_flag::ERROR == ast::type_flag::ERROR) { bucket = &sorted.errors; }; append(bucket, ast::decl { exported = false, start = decl.start, end = decl.end, - decl = alloc([t[j]]), + decl = alloc([t]), docs = decl.docs, }); }; - case let c: []ast::decl_const => - for (let j = 0z; j < len(c); j += 1) { + case let consts: []ast::decl_const => + for (let c .. consts) { append(sorted.constants, ast::decl { exported = false, start = decl.start, end = decl.end, - decl = alloc([c[j]]), + decl = alloc([c]), docs = decl.docs, }); }; - case let g: []ast::decl_global => - for (let j = 0z; j < len(g); j += 1) { + case let globals: []ast::decl_global => + for (let g .. globals) { append(sorted.globals, ast::decl { exported = false, start = decl.start, end = decl.end, decl = alloc([ast::decl_global { - is_const = g[j].is_const, - is_threadlocal = g[j].is_threadlocal, - symbol = g[j].symbol, - ident = g[j].ident, - _type = g[j]._type, + is_const = g.is_const, + is_threadlocal = g.is_threadlocal, + symbol = g.symbol, + ident = g.ident, + _type = g._type, init = null, }]), docs = decl.docs, diff --git a/cmd/haredoc/doc/tty.ha b/cmd/haredoc/doc/tty.ha @@ -45,13 +45,11 @@ export fn emit_tty(ctx: *context) (void | error) = { let readme = bufio::init(readme, rbuf, []); let sc = bufio::newscanner(&readme, types::SIZE_MAX); defer bufio::finish(&sc); - for (true) match (bufio::scan_line(&sc)?) { - case io::EOF => break; - case let s: const str => + for (let line => bufio::scan_line(&sc)?) { firstline = false; if (!no_color) fmt::fprintf(ctx.out, "\x1b[{}m", color(unparse::synkind::COMMENT))?; - fmt::fprint(ctx.out, "//", s)?; + fmt::fprint(ctx.out, "//", line)?; if (!no_color) fmt::fprint(ctx.out, "\x1b[m")?; fmt::fprintln(ctx.out)?; }; @@ -62,28 +60,28 @@ export fn emit_tty(ctx: *context) (void | error) = { // XXX: Should we emit the dependencies, too? let printed = false; - for (let i = 0z; i < len(summary.types); i += 1) { - if (details_tty(ctx, &summary.types[i])?) { + for (let t &.. summary.types) { + if (details_tty(ctx, t)?) { printed = true; }; }; - for (let i = 0z; i < len(summary.constants); i += 1) { - if (details_tty(ctx, &summary.constants[i])?) { + for (let c &.. summary.constants) { + if (details_tty(ctx, c)?) { printed = true; }; }; - for (let i = 0z; i < len(summary.errors); i += 1) { - if (details_tty(ctx, &summary.errors[i])?) { + for (let e &.. summary.errors) { + if (details_tty(ctx, e)?) { printed = true; }; }; - for (let i = 0z; i < len(summary.globals); i += 1) { - if (details_tty(ctx, &summary.globals[i])?) { + for (let g &.. summary.globals) { + if (details_tty(ctx, g)?) { printed = true; }; }; - for (let i = 0z; i < len(summary.funcs); i += 1) { - if (details_tty(ctx, &summary.funcs[i])?) { + for (let f &.. summary.funcs) { + if (details_tty(ctx, f)?) { printed = true; }; }; @@ -113,13 +111,13 @@ fn emit_submodules_tty(ctx: *context) (void | error) = { } else { fmt::fprintln(ctx.out, "// Submodules")?; }; - for (let i = 0z; i < len(ctx.submods); i += 1) { + for (let submodule .. ctx.submods) { let submodule = if (len(ctx.ident) != 0) { const s = unparse::identstr(ctx.ident); defer free(s); - yield strings::concat(s, "::", ctx.submods[i]); + yield strings::concat(s, "::", submodule); } else { - yield strings::dup(ctx.submods[i]); + yield strings::dup(submodule); }; defer free(submodule); diff --git a/cmd/haredoc/doc/types.ha b/cmd/haredoc/doc/types.ha @@ -70,23 +70,23 @@ export type summary = struct { }; export fn finish_summary(s: summary) void = { - for (let i = 0z; i < len(s.constants); i += 1) { - free(s.constants[i].decl as []ast::decl_const); + for (let c .. s.constants) { + free(c.decl as []ast::decl_const); }; free(s.constants); - for (let i = 0z; i < len(s.errors); i += 1) { - free(s.errors[i].decl as []ast::decl_type); + for (let e .. s.errors) { + free(e.decl as []ast::decl_type); }; free(s.errors); - for (let i = 0z; i < len(s.types); i += 1) { - free(s.types[i].decl as []ast::decl_type); + for (let t .. s.types) { + free(t.decl as []ast::decl_type); }; free(s.types); - for (let i = 0z; i < len(s.globals); i += 1) { - free(s.globals[i].decl as []ast::decl_global); + for (let g .. s.globals) { + free(g.decl as []ast::decl_global); }; free(s.globals); diff --git a/cmd/haredoc/doc/util.ha b/cmd/haredoc/doc/util.ha @@ -17,13 +17,7 @@ let firstline: bool = true; fn trim_comment(s: str) str = { let trimmed = memio::dynamic(); let tok = strings::tokenize(s, "\n"); - for (true) { - const line = match (strings::next_token(&tok)) { - case void => - break; - case let line: str => - yield line; - }; + for (let line => strings::next_token(&tok)) { memio::concat(&trimmed, strings::trimprefix(line, " "), "\n")!; }; return strings::dup(memio::string(&trimmed)!); @@ -34,14 +28,11 @@ export fn submodules(path: str, show_undocumented: bool) ([]str | error) = { let it = os::iter(path)?; defer fs::finish(it); let pathbuf = path::init(path)!; - for (true) match (module::next(it)?) { - case let d: fs::dirent => + for (let d => module::next(it)?) { path::set(&pathbuf, path, d.name, "README")!; if (show_undocumented || os::exists(path::string(&pathbuf))) { append(submodules, strings::dup(d.name)); }; - case void => - break; }; sort::sort(submodules, size(str), &cmp::strs); return submodules; diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha @@ -60,20 +60,19 @@ fn doc(name: str, cmd: *getopt::command) (void | error) = { let tags: []str = default_tags(); defer free(tags); - for (let i = 0z; i < len(cmd.opts); i += 1) { - let opt = cmd.opts[i]; - switch (opt.0) { + for (let (k, v) .. cmd.opts) { + switch (k) { case 'F' => - switch (opt.1) { + switch (v) { case "tty" => html = false; case "html" => html = true; case => - fmt::fatal("Invalid format", opt.1); + fmt::fatal("Invalid format", v); }; case 'T' => - merge_tags(&tags, opt.1)?; + merge_tags(&tags, v)?; case 't' => template = false; case 'a' => @@ -148,32 +147,33 @@ fn doc(name: str, cmd: *getopt::command) (void | error) = { let decls: []ast::decl = []; defer { - for (let i = 0z; i < len(decls); i += 1) { - ast::decl_finish(decls[i]); + for (let decl .. decls) { + ast::decl_finish(decl); }; free(decls); }; if (declpath != "") { - for (let i = 0z; i < len(declsrcs.ha); i += 1) { - let u = doc::scan(declsrcs.ha[i])?; + for (let ha .. declsrcs.ha) { + let u = doc::scan(ha)?; ast::imports_finish(u.imports); append(decls, u.decls...); }; let matching: []ast::decl = []; let notmatching: []ast::decl = []; - for (let i = 0z; i < len(decls); i += 1) { - if (has_decl(decls[i], id[len(id) - 1])) { - append(matching, decls[i]); + + for (let decl .. decls) { + if (has_decl(decl, id[len(id) - 1])) { + append(matching, decl); } else { - append(notmatching, decls[i]); + append(notmatching, decl); }; }; get_init_funcs(&matching, &notmatching); - for (let i = 0z; i < len(notmatching); i += 1) { - ast::decl_finish(notmatching[i]); + for (let decl .. notmatching) { + ast::decl_finish(decl); }; free(notmatching); free(decls); @@ -199,8 +199,8 @@ fn doc(name: str, cmd: *getopt::command) (void | error) = { const ambiguous = modpath != "" && len(decls) > 0; if (len(decls) == 0) { - for (let i = 0z; i < len(modsrcs.ha); i += 1) { - let u = doc::scan(modsrcs.ha[i])?; + for (let ha .. modsrcs.ha) { + let u = doc::scan(ha)?; ast::imports_finish(u.imports); append(decls, u.decls...); }; @@ -343,9 +343,9 @@ fn has_decl(decl: ast::decl, name: str) bool = { }; match (decl.decl) { - case let d: []ast::decl_const => - for (let i = 0z; i < len(d); i += 1) { - if (len(d[i].ident) == 1 && d[i].ident[0] == name) { + case let consts: []ast::decl_const => + for (let d .. consts) { + if (len(d.ident) == 1 && d.ident[0] == name) { return true; }; }; @@ -355,25 +355,25 @@ fn has_decl(decl: ast::decl, name: str) bool = { }; let tok = strings::rtokenize(d.symbol, "."); match (strings::next_token(&tok)) { - case void => void; + case done => void; case let s: str => return s == name; }; - case let d: []ast::decl_global => - for (let i = 0z; i < len(d); i += 1) { - if (len(d[i].ident) == 1 && d[i].ident[0] == name) { + case let globals: []ast::decl_global => + for (let d .. globals) { + if (len(d.ident) == 1 && d.ident[0] == name) { return true; }; - let tok = strings::rtokenize(d[i].symbol, "."); + let tok = strings::rtokenize(d.symbol, "."); match (strings::next_token(&tok)) { - case void => void; + case done => void; case let s: str => return s == name; }; }; - case let d: []ast::decl_type => - for (let i = 0z; i < len(d); i += 1) { - if (len(d[i].ident) == 1 && d[i].ident[0] == name) { + case let types: []ast::decl_type => + for (let d .. types) { + if (len(d.ident) == 1 && d.ident[0] == name) { return true; }; }; @@ -479,8 +479,8 @@ fn is_init_type(ident: ast::ident, _type: *ast::_type) bool = { return false; }; case let repr: ast::tagged_type => - for (let i = 0z; i < len(repr); i += 1) { - if (is_init_type(ident, repr[i])) { + for (let t .. repr) { + if (is_init_type(ident, t)) { return true; }; }; diff --git a/cmd/haredoc/util.ha b/cmd/haredoc/util.ha @@ -17,17 +17,17 @@ fn merge_tags(current: *[]str, new: str) (void | module::error) = { }; let newtags = module::parse_tags(trimmed)?; defer free(newtags); - for :new (let i = 0z; i < len(newtags); i += 1) { - for (let j = 0z; j < len(current); j += 1) { - if (newtags[i].name == current[j]) { - if (!newtags[i].include) { - static delete(current[j]); + for :new (let newtag .. newtags) { + for (let i = 0z; i < len(current); i += 1) { + if (newtag.name == current[i]) { + if (!newtag.include) { + static delete(current[i]); }; continue :new; }; }; - if (newtags[i].include) { - append(current, newtags[i].name); + if (newtag.include) { + append(current, newtag.name); }; }; }; diff --git a/cmd/parsechk/main.ha b/cmd/parsechk/main.ha @@ -22,10 +22,7 @@ export fn main() void = { fn iter(buf: *path::buffer, status: *int) void = { let it = os::iter(path::string(buf))!; defer fs::finish(it); - for (true) match (fs::next(it)!) { - case void => - break; - case let ent: fs::dirent => + for (let ent => fs::next(it)!) { path::push(buf, ent.name)!; defer path::pop(buf); const st = os::stat(path::string(buf))!; diff --git a/encoding/base64/base64.ha b/encoding/base64/base64.ha @@ -150,8 +150,8 @@ fn writeavail(s: *encoder) (void | io::error) = { // Flushes pending writes to the underlying stream. fn encode_closer(s: *io::stream) (void | io::error) = { let s = s: *encoder; - let done = false; - defer if (done) clear(s); + let finished = false; + defer if (finished) clear(s); match (s.err) { case let e: io::error => @@ -164,12 +164,12 @@ fn encode_closer(s: *io::stream) (void | io::error) = { for (s.oavail > 0) { writeavail(s)?; }; - done = true; + finished = true; return; }; if (s.iavail == 0) { - done = true; + finished = true; return; }; @@ -191,7 +191,7 @@ fn encode_closer(s: *io::stream) (void | io::error) = { for (s.oavail > 0) { writeavail(s)?; }; - done = true; + finished = true; }; fn clear(e: *encoder) void = { diff --git a/fmt/iter.ha b/fmt/iter.ha @@ -55,10 +55,10 @@ fn iter(fmt: str, args: []field) iterator = iterator { checkunused = true, }; -fn next(it: *iterator) (str | (formattable, mods) | void) = { +fn next(it: *iterator) (str | (formattable, mods) | done) = { let r = match (strings::next(&it.iter)) { - case void => - return void; + case done => + return done; case let r: rune => yield r; }; @@ -66,7 +66,7 @@ fn next(it: *iterator) (str | (formattable, mods) | void) = { case '{' => void; // handled below case '}' => match (strings::next(&it.iter)) { - case void => + case done => abort("Invalid format string (hanging '}')"); case let r: rune => assert(r == '}', "Invalid format string (hanging '}')"); @@ -75,9 +75,7 @@ fn next(it: *iterator) (str | (formattable, mods) | void) = { case => strings::prev(&it.iter); let start = it.iter; - for (true) match (strings::next(&it.iter)) { - case void => break; - case let r: rune => + for (let r => strings::next(&it.iter)) { if (r == '{' || r == '}') { strings::prev(&it.iter); break; @@ -189,7 +187,7 @@ fn scan_sz(it: *iterator) size = { }; fn getrune(it: *iterator) rune = match (strings::next(&it.iter)) { -case void => +case done => abort("Invalid format string (unterminated '{')"); case let r: rune => return r; diff --git a/fmt/print.ha b/fmt/print.ha @@ -31,7 +31,7 @@ export fn fprintf( let n = 0z, i = 0z; let it = iter(fmt, args); for (true) match (next(&it)) { - case void => break; + case done => break; case let s: str => n += format(h, s, &mods { ... })?; case let f: (formattable, mods) => diff --git a/fnmatch/+test.ha b/fnmatch/+test.ha @@ -185,8 +185,7 @@ const testcases: [_]testcase = [ ]; @test fn fnmatch() void = { - for (let i = 0z; i < len(testcases); i += 1) { - let tc = testcases[i]; + for (let tc .. testcases) { assert(fnmatch(tc.0, tc.1, tc.3...) == tc.2); }; diff --git a/fnmatch/fnmatch.ha b/fnmatch/fnmatch.ha @@ -45,8 +45,8 @@ type token = (rune | bracket | star | question | end); // [[fnmatch]]. For an explanation of their meaning, see [[flag]]. export fn fnmatch(pattern: str, string: str, flags: flag...) bool = { let fl: flag = 0; - for (let i = 0z; i < len(flags); i += 1) { - fl |= flags[i]; + for (let flag .. flags) { + fl |= flag; }; let b = if (fl & flag::PATHNAME != 0) { yield fnmatch_pathname(pattern, string, fl); @@ -78,7 +78,7 @@ fn fnmatch_pathname( case (question | star) => void; }; let s = match (strings::next_token(&tok)) { - case void => + case done => return false; case let s: str => yield s; @@ -91,13 +91,13 @@ fn fnmatch_pathname( }; }; let s = match(strings::next_token(&tok)) { - case void => + case done => return false; case let s: str => yield s; }; let p = strings::iterstr(&start); - return fnmatch_internal(p, s, fl)? && strings::next_token(&tok) is void; + return fnmatch_internal(p, s, fl)? && strings::next_token(&tok) is done; }; // Core fnmatch function, implementing the "Sea of stars" algorithm that is also @@ -128,7 +128,7 @@ fn fnmatch_internal( case star => break; case end => - return rn is void; + return rn is done; case question => yield rn is rune; case bracket => @@ -170,7 +170,7 @@ fn fnmatch_internal( let rn = strings::prev(&s); let matches = match (pat_next(&p, fl)?) { case end => - if (rn is void) { + if (rn is done) { break; } else { return false; @@ -221,7 +221,7 @@ fn fnmatch_internal( s = copy; }; match (strings::next(&s_copy)) { - case void => + case done => return false; case rune => void; }; @@ -326,7 +326,7 @@ fn match_ctype(it: *strings::iterator, c: rune) (bool | errors::invalid) = { fn pat_next(pat: *strings::iterator, fl: flag) (token | errors::invalid) = { let r = match (strings::next(pat)) { - case void => + case done => return end; case let r: rune => yield r; @@ -351,7 +351,7 @@ fn advance_or_err(it: *strings::iterator) (rune | errors::invalid) = { match (strings::next(it)) { case let r: rune => return r; - case void => + case done => return errors::invalid; }; }; diff --git a/fs/fs.ha b/fs/fs.ha @@ -315,4 +315,4 @@ export fn symlink(fs: *fs, target: str, path: str) (void | error) = { // not guaranteed to be complete when an error has been returned. The file stat // returned may only have the type bits set on the file mode; callers should // call [[stat]] to obtain the detailed file mode. -export fn next(iter: *iterator) (dirent | void | error) = iter.next(iter); +export fn next(iter: *iterator) (dirent | done | error) = iter.next(iter); diff --git a/fs/types.ha b/fs/types.ha @@ -312,7 +312,7 @@ export type fs = struct { }; // A function which returns the next directory from an [[iterator]]. -export type nextfunc = fn(iter: *iterator) (dirent | void | error); +export type nextfunc = fn(iter: *iterator) (dirent | done | error); // A function which frees state associated with an [[iterator]]. export type finishfunc = fn(iter: *iterator) void; diff --git a/fs/util.ha b/fs/util.ha @@ -87,23 +87,19 @@ export fn readdir(fs: *fs, path: str) ([]dirent | error) = { let i = iter(fs, path)?; defer finish(i); let ents: []dirent = []; - for (true) { - match (next(i)) { - case let d: dirent => - append(ents, dirent_dup(&d)); - case void => - break; - }; + + for (let d => next(i)?) { + append(ents, dirent_dup(&d)); }; return ents; }; // Frees a slice of [[dirent]]s. -export fn dirents_free(d: []dirent) void = { - for (let i = 0z; i < len(d); i += 1) { - dirent_finish(&d[i]); +export fn dirents_free(dirents: []dirent) void = { + for (let d &.. dirents) { + dirent_finish(d); }; - free(d); + free(dirents); }; // Removes a directory, and anything in it. @@ -115,21 +111,16 @@ export fn rmdirall(fs: *fs, path: str) (void | error) = { fn rmdirall_path(fs: *fs, buf: *path::buffer) (void | error) = { let it = iter(fs, path::string(buf))?; defer finish(it); - for (true) { - match (next(it)) { - case let ent: dirent => - path::push(buf, ent.name)!; - - switch (ent.ftype & mode::DIR) { - case mode::DIR => - rmdirall_path(fs, buf)?; - case => - remove(fs, path::string(buf))?; - }; - path::pop(buf); - case void => - break; + for (let ent => next(it)?) { + path::push(buf, ent.name)!; + + switch (ent.ftype & mode::DIR) { + case mode::DIR => + rmdirall_path(fs, buf)?; + case => + remove(fs, path::string(buf))?; }; + path::pop(buf); }; return rmdir(fs, path::string(buf)); }; @@ -143,15 +134,10 @@ export fn realpath(fs: *fs, path: str) (str | error) = { static let pathbuf = path::buffer { ... }; path::set(&pathbuf, resolve(fs, path))!; const iter = path::iter(&pathbuf); - for (true) { - const item = match (path::nextiter(&iter)) { - case let item: str => - yield item; - case void => - break; - }; + for (let item => path::nextiter(&iter)) { const item = path::push(&res, item)!; + const link = match (readlink(fs, item)) { case let link: str => yield link; diff --git a/getopt/getopts.ha b/getopt/getopts.ha @@ -115,9 +115,7 @@ export fn tryparse(args: []str, help: help...) (command | error) = { let iter = strings::iter(arg); assert(strings::next(&iter) as rune == '-'); - for (true) match (strings::next(&iter)) { - case void => break; - case let r: rune => + for (let r => strings::next(&iter)) { let found = false; for (let j = 0z; j < len(help); j += 1) match (help[j]) { case let f: flag_help => @@ -217,26 +215,32 @@ fn _printusage( z += fmt::fprint(out, " [-h")?; started_flags = true; }; - for (let i = 0z; i < len(help); i += 1) match (help [i]) { - case let h: flag_help => - if (!started_flags) { - z += fmt::fprint(out, " [-")?; - started_flags = true; + for (let h .. help) { + match (h) { + case let h: flag_help => + if (!started_flags) { + z += fmt::fprint(out, " [-")?; + started_flags = true; + }; + z += fmt::fprint(out, h.0)?; + case => void; }; - z += fmt::fprint(out, h.0)?; - case => void; }; if (started_flags) { z += fmt::fprint(out, "]")?; }; - for (let i = 0z; i < len(help); i += 1) if (help[i] is parameter_help) { - const help = help[i] as parameter_help; - if (indent) { - z += fmt::fprintf(out, "\n\t")?; + for (let h .. help) { + match (h) { + case let h: parameter_help => + if (indent) { + z += fmt::fprintf(out, "\n\t")?; + }; + z += fmt::fprintf(out, " [-{} <{}>]", h.0, h.1)?; + case => void; }; - z += fmt::fprintf(out, " [-{} <{}>]", help.0, help.1)?; }; + let first_arg = true; for (let i = 1z; i < len(help); i += 1) if (help[i] is cmd_help) { if (first_arg) { @@ -252,8 +256,8 @@ fn _printusage( }; fn contains_h(help: []help) bool = { - for (let i = 0z; i < len(help); i += 1) { - const r = match (help[i]) { + for (let h .. help) { + const r = match (h) { case let h: flag_help => yield h.0; case let h: parameter_help => yield h.0; case => continue; @@ -287,12 +291,14 @@ export fn printhelp( if (!contains_h) { fmt::fprintln(out, "-h: print this help text")?; }; - for (let i = 0z; i < len(help); i += 1) match (help[i]) { - case let f: flag_help => - fmt::fprintfln(out, "-{}: {}", f.0, f.1)?; - case let p: parameter_help => - fmt::fprintfln(out, "-{} <{}>: {}", p.0, p.1, p.2)?; - case => void; + for (let h .. help) { + match (h) { + case let f: flag_help => + fmt::fprintfln(out, "-{}: {}", f.0, f.1)?; + case let p: parameter_help => + fmt::fprintfln(out, "-{} <{}>: {}", p.0, p.1, p.2)?; + case => void; + }; }; printsubcmds(out, help)?; @@ -300,19 +306,22 @@ export fn printhelp( fn printsubcmds(out: io::handle, help: []help) (void | io::error) = { let first = true; - for (let i = 0z; i < len(help); i += 1) match (help[i]) { - case let s: subcmd_help => - // Only print this if there are subcommands to show - if (first) { - fmt::fprintln(out, "\nSubcommands:")?; - first = false; - }; - if (len(s.1) == 0 || !(s.1[0] is cmd_help)) { - fmt::fprintfln(out, " {}", s.0)?; - } else { - fmt::fprintfln(out, " {}: {}", s.0, s.1[0] as cmd_help: str)?; + for (let h .. help) { + match (h) { + case let s: subcmd_help => + // Only print this if there are subcommands to show + if (first) { + fmt::fprintln(out, "\nSubcommands:")?; + first = false; + }; + if (len(s.1) == 0 || !(s.1[0] is cmd_help)) { + fmt::fprintfln(out, " {}", s.0)?; + } else { + fmt::fprintfln(out, " {}: {}", s.0, + s.1[0] as cmd_help: str)?; + }; + case => void; }; - case => void; }; }; diff --git a/glob/+test.ha b/glob/+test.ha @@ -23,23 +23,23 @@ use strings; ("..", flag::NONE), ("\\*", flag::NONE), ]; - for (let i = 0z; i < len(cases); i += 1) { - let gen = glob(cases[i].0, cases[i].1); + for (let c .. cases) { + let gen = glob(c.0, c.1); defer finish(&gen); for (true) match (next(&gen)) { - case void => + case done => break; case failure => continue; case let s: str => let bs = fnmatch::flag::PATHNAME; - if (cases[i].1 & flag::NOESCAPE != 0) { + if (c.1 & flag::NOESCAPE != 0) { bs |= fnmatch::flag::NOESCAPE; }; - assert(fnmatch::fnmatch(cases[i].0, s, bs) - || cases[i].1 & flag::MARK != 0 + assert(fnmatch::fnmatch(c.0, s, bs) + || c.1 & flag::MARK != 0 && fnmatch::fnmatch( - cases[i].0, + c.0, strings::rtrim(s, '/'), bs ) @@ -82,13 +82,13 @@ use strings; ]; let p = pattern_init(); defer pattern_free(&p); - for (let i = 0z; i < len(cases); i += 1) { - pattern_parse(&p, cases[i].0, cases[i].1); + for (let c .. cases) { + pattern_parse(&p, c.0, c.1); const dir = pattern_dir(&p); const pat = pattern_pat(&p); const rem = pattern_rem(&p); - assert(dir == cases[i].2); - assert(pat == cases[i].3); - assert(rem == cases[i].4); + assert(dir == c.2); + assert(pat == c.3); + assert(rem == c.4); }; }; diff --git a/glob/glob.ha b/glob/glob.ha @@ -68,8 +68,8 @@ export fn glob(pattern: str, flags: flag...) generator = { let ss = strstack_init(); memio::concat(strstack_push(&ss), pattern)!; let bs = flag::NONE; - for (let i = 0z; i < len(flags); i += 1) { - bs |= flags[i]; + for (let flag .. flags) { + bs |= flag; }; return generator { pats = ss, @@ -89,7 +89,7 @@ export fn finish(gen: *generator) void = { // is called again. If, during the search, a directory is encountered that // cannot be opened or read, a [[failure]] object is returned instead. // [[next]] can be repeatedly called until void is returned. -export fn next(gen: *generator) (str | void | failure) = { +export fn next(gen: *generator) (str | done | failure) = { const init = strstack_size(&gen.pats) == 1 && len(memio::string(&gen.tmpp.dir)!) == 0 && len(memio::string(&gen.tmpp.pat)!) == 0 @@ -102,6 +102,7 @@ export fn next(gen: *generator) (str | void | failure) = { if (init && gen.flgs & flag::NOCHECK != 0) { return memio::string(&gen.pats.bufv[0])!; }; + return done; }; fn next_match(gen: *generator) (str | void | failure) = { @@ -145,7 +146,7 @@ fn next_match(gen: *generator) (str | void | failure) = { defer fs::finish(it); for (true) match (fs::next(it)) { - case void => + case done => break; case let de: fs::dirent => if (patm && !fs::isdir(de.ftype) && !fs::islink(de.ftype)) { @@ -233,7 +234,7 @@ fn pattern_parse(p: *pattern, pstr: str, noesc: bool) void = { // characters. for (let brk = false, esc = false; true) { const r = match (strings::next(&itdir)) { - case void => + case done => memio::concat(&p.dir, memio::string(&p.pat)!)!; memio::reset(&p.pat); return; @@ -270,14 +271,9 @@ fn pattern_parse(p: *pattern, pstr: str, noesc: bool) void = { // p.pat is the first path component which contains special // characters. memio::reset(&p.pat); - for (let esc = false; true) { - const r = match (strings::next(&itpat)) { - case void => - return; - case let r: rune => - yield r; - }; + let esc = false; + for (let r => strings::next(&itpat)) { if (!esc && r == '\\' && !noesc) { esc = true; continue; @@ -302,8 +298,8 @@ fn strstack_init() strstack = strstack { }; fn strstack_free(ss: *strstack) void = { - for (let i = 0z; i < len(ss.bufv); i += 1) { - io::close(&ss.bufv[i])!; + for (let stream &.. ss.bufv) { + io::close(stream)!; }; free(ss.bufv); }; diff --git a/hare/ast/expr.ha b/hare/ast/expr.ha @@ -241,7 +241,7 @@ export type tuple_literal = []*expr; export type _null = void; // A scalar value. -export type value = (bool | _null | str | rune | void); +export type value = (bool | done | _null | str | rune | void); // An integer or float literal. export type number_literal = struct { @@ -273,12 +273,24 @@ export type delete_expr = struct { is_static: bool, }; +// The kind of for expression being used. +export type for_kind = enum { + ACCUMULATOR, + EACH_VALUE, + EACH_POINTER, + ITERATOR, +}; + // A for loop. // // for (let foo = 0; foo < bar; baz) quux +// for (let line => next_line()) quux +// for (let number .. [1, 2, 3]) quux +// for (let ptr &.. [1, 2, 3]) quux export type for_expr = struct { + kind: for_kind, bindings: nullable *expr, - cond: *expr, + cond: nullable *expr, afterthought: nullable *expr, body: *expr, label: label, diff --git a/hare/ast/type.ha b/hare/ast/type.ha @@ -11,8 +11,8 @@ export type alias_type = struct { // A built-in primitive type (int, bool, str, etc). export type builtin_type = enum { - BOOL, F32, F64, FCONST, I16, I32, I64, I8, ICONST, INT, NEVER, NULL, - OPAQUE, RCONST, RUNE, SIZE, STR, U16, U32, U64, U8, UINT, UINTPTR, + BOOL, DONE, F32, F64, FCONST, I16, I32, I64, I8, ICONST, INT, NEVER, + NULL, OPAQUE, RCONST, RUNE, SIZE, STR, U16, U32, U64, U8, UINT, UINTPTR, VALIST, VOID, }; diff --git a/hare/lex/+test.ha b/hare/lex/+test.ha @@ -136,7 +136,7 @@ fn loc(line: uint, col: uint) location = location { const in = ". .. ... < << <= <<= > >> >= >>= >>"; const expected: [_]token = [ (ltok::DOT, void, loc(1, 1)), - (ltok::SLICE, void, loc(1, 3)), + (ltok::DOUBLE_DOT, void, loc(1, 3)), (ltok::ELLIPSIS, void, loc(1, 6)), (ltok::LESS, void, loc(1, 10)), (ltok::LSHIFT, void, loc(1, 12)), diff --git a/hare/lex/lex.ha b/hare/lex/lex.ha @@ -689,7 +689,7 @@ fn lex3(lex: *lexer) (token | error) = { lex.require_int = true; yield ltok::DOT; } else if (try(lex, '.')? is void) { - yield ltok::SLICE; + yield ltok::DOUBLE_DOT; } else ltok::ELLIPSIS; line_comment(lex)?; return (tok, void, r.1); diff --git a/hare/lex/token.ha b/hare/lex/token.ha @@ -31,6 +31,7 @@ export type ltok = enum uint { DEF, DEFER, DELETE, + DONE, ELSE, ENUM, EXPORT, @@ -97,6 +98,7 @@ export type ltok = enum uint { DIVEQ, DOT, DOUBLE_COLON, + DOUBLE_DOT, ELLIPSIS, EQUAL, GT, @@ -130,7 +132,6 @@ export type ltok = enum uint { RSHIFT, RSHIFTEQ, SEMICOLON, - SLICE, TIMES, TIMESEQ, LAST_BTOK = TIMESEQ, @@ -182,6 +183,7 @@ const bmap: [_]str = [ "def", "defer", "delete", + "done", "else", "enum", "export", @@ -244,6 +246,7 @@ const bmap: [_]str = [ "/=", ".", "::", + "..", "...", "=", ">", @@ -277,7 +280,6 @@ const bmap: [_]str = [ ">>", ">>=", ";", - "..", "*", "*=", ]; diff --git a/hare/module/deps.ha b/hare/module/deps.ha @@ -30,24 +30,24 @@ export type module = struct { // The list will be sorted alphabetically and deduplicated. export fn parse_deps(files: str...) ([]ast::ident | error) = { let deps: []ast::ident = []; - for (let i = 0z; i < len(files); i += 1) { - let handle = match (os::open(files[i])) { + for (let file .. files) { + let handle = match (os::open(file)) { case let f: io::file => yield f; case let e: fs::error => - return attach(strings::dup(files[i]), e); + return attach(strings::dup(file), e); }; defer io::close(handle)!; let sc = bufio::newscanner(handle, types::SIZE_MAX); defer bufio::finish(&sc); - let lexer = lex::init(&sc, files[i]); + let lexer = lex::init(&sc, file); let imports = parse::imports(&lexer)?; defer ast::imports_finish(imports); // dedupe + insertion sort - for (let i = 0z; i < len(imports); i += 1) { - let id = imports[i].ident; + for (let import &.. imports) { + let id = import.ident; let idx = sort::rbisect(deps, size(ast::ident), &id, &idcmp); if (idx == 0 || idcmp(&deps[idx - 1], &id) != 0) { insert(deps[idx], ast::ident_dup(id)); @@ -95,16 +95,13 @@ fn get_deps(cachedir: str, srcs: *srcset) ([]ast::ident | error) = { deps = parse_deps(srcs.ha...)?; io::trunc(depsfile, 0)?; let out = bufio::init(depsfile, [], buf.buf); - for (let i = 0z; i < len(deps); i += 1) { - unparse::ident(&out, deps[i])?; + for (let dep .. deps) { + unparse::ident(&out, dep)?; fmt::fprintln(&out)?; }; } else { let in = bufio::newscanner_static(depsfile, buf.buf); - for (true) match (bufio::scan_line(&in)?) { - case io::EOF => - break; - case let s: const str => + for (let s => bufio::scan_line(&in)?) { append(deps, parse::identstr(s)?); }; }; @@ -193,16 +190,16 @@ export fn finish(mod: *module) void = { ast::ident_free(mod.ns); free(mod.path); finish_srcset(&mod.srcs); - for (let i = 0z; i < len(mod.deps); i += 1) { - ast::ident_free(mod.deps[i].1); + for (let (_, ident) .. mod.deps) { + ast::ident_free(ident); }; free(mod.deps); }; // Free all the [[module]]s in a slice of modules, and then the slice itself. export fn free_slice(mods: []module) void = { - for (let i = 0z; i < len(mods); i += 1) { - finish(&mods[i]); + for (let mod &.. mods) { + finish(mod); }; free(mods); }; diff --git a/hare/module/format.ha b/hare/module/format.ha @@ -15,15 +15,15 @@ export fn format_tags( let n = 0z; match (tags) { case let tags: []str => - for (let i = 0z; i < len(tags); i += 1) { - n += fmt::fprintf(out, "+{}", tags[i])?; + for (let tag .. tags) { + n += fmt::fprintf(out, "+{}", tag)?; }; case let tags: []tag => - for (let i = 0z; i < len(tags); i += 1) { + for (let tag .. tags) { n += fmt::fprintf( out, - if (tags[i].include) "+{}" else "-{}", - tags[i].name)?; + if (tag.include) "+{}" else "-{}", + tag.name)?; }; }; return n; @@ -37,18 +37,22 @@ export fn format_srcset(out: io::handle, srcs: *srcset) (size | io::error) = { n += fmt::fprintln(out)?; const dt = date::from_instant(chrono::LOCAL, srcs.mtime); n += date::format(out, "last change to source list: %F %T\n", &dt)?; + n += fmt::fprintln(out, "hare sources:")?; - for (let i = 0z; i < len(srcs.ha); i += 1) { - n += fmt::fprintln(out, " ", srcs.ha[i])?; + for (let ha .. srcs.ha) { + n += fmt::fprintln(out, " ", ha)?; }; + n += fmt::fprintln(out, "assembly sources:")?; - for (let i = 0z; i < len(srcs.s); i += 1) { - n += fmt::fprintln(out, " ", srcs.s[i])?; + for (let s .. srcs.s) { + n += fmt::fprintln(out, " ", s)?; }; + n += fmt::fprintln(out, "object sources:")?; - for (let i = 0z; i < len(srcs.o); i += 1) { - n += fmt::fprintln(out, " ", srcs.o[i])?; + for (let o .. srcs.o) { + n += fmt::fprintln(out, " ", o)?; }; + return n; }; @@ -59,9 +63,9 @@ export fn format(out: io::handle, mod: *module) (size | io::error) = { n += fmt::fprintln(out, "path:", mod.path)?; n += format_srcset(out, &mod.srcs)?; n += fmt::fprintln(out, "dependencies:")?; - for (let i = 0z; i < len(mod.deps); i += 1) { + for (let (_, ident) .. mod.deps) { n += fmt::fprint(out, " ")?; - n += unparse::ident(out, mod.deps[i].1)?; + n += unparse::ident(out, ident)?; n += fmt::fprintln(out)?; }; return n; diff --git a/hare/module/srcs.ha b/hare/module/srcs.ha @@ -63,9 +63,9 @@ export fn find(ctx: *context, loc: location) ((str, srcset) | error) = { case let e: error => return e; }; - case let mod: ast::ident => + case let ident: ast::ident => let tok = strings::tokenize(ctx.harepath, ":"); - let next: (str | void) = "."; + let next: (str | done) = "."; for (next is str; next = strings::next_token(&tok)) { if (!os::exists(next as str)) { continue; @@ -73,8 +73,8 @@ export fn find(ctx: *context, loc: location) ((str, srcset) | error) = { static let buf = path::buffer { ... }; path::set(&buf, os::realpath(next as str)?)?; - for (let i = 0z; i < len(mod); i += 1) { - path::push(&buf, mod[i])?; + for (let part .. ident) { + path::push(&buf, part)?; }; match (path_find(ctx, &buf)) { @@ -85,7 +85,7 @@ export fn find(ctx: *context, loc: location) ((str, srcset) | error) = { return attach(strings::dup(path::string(&buf)), e); }; }; - return attach(locstr(mod), not_found); + return attach(locstr(ident), not_found); }; }; @@ -277,10 +277,8 @@ fn _findsrcs( return attach(strings::dup(pathstr), e); }; defer fs::finish(iter); - for (true) match (fs::next(iter)?) { - case void => - break; - case let d: fs::dirent => + + for (let d => fs::next(iter)?) { path::push(buf, d.name)?; defer path::pop(buf); @@ -338,13 +336,16 @@ export fn parse_tags(s: str) ([]tag | error) = { // Checks if a set of tags are compatible with a tag requirement. export fn tags_compat(have: []str, want: []tag) bool = { - for (let i = 0z; i < len(want); i += 1) { - let t = want[i]; + for (let t .. want) { let found = false; - for (let j = 0z; j < len(have); j += 1) if (have[j] == t.name) { - found = true; - break; + + for (let ht .. have) { + if (ht == t.name) { + found = true; + break; + }; }; + if (t.include ^^ found) { return false; }; @@ -355,14 +356,17 @@ export fn tags_compat(have: []str, want: []tag) bool = { // same as tags_compat, but also adds any relevant tags to a seentags list // for use in _findsrcs. fn seentags_compat(have: []str, want: []tag, seen: *[]str) bool = { - for (let i = 0z; i < len(want); i += 1) { - let t = want[i]; + for (let t .. want) { let found = false; - for (let j = 0z; j < len(have); j += 1) if (have[j] == t.name) { - insert_uniq(seen, t.name); - found = true; - break; + + for (let ht .. have) { + if (ht == t.name) { + insert_uniq(seen, t.name); + found = true; + break; + }; }; + if (t.include ^^ found) { return false; }; diff --git a/hare/module/util.ha b/hare/module/util.ha @@ -33,23 +33,22 @@ export fn outdated(target: str, deps: []str, mtime: time::instant) bool = { if (time::compare(current, mtime) < 0) { return true; }; - for (let i = 0z; i < len(deps); i += 1) match (os::stat(deps[i])) { - case fs::error => - continue; - case let stat: fs::filestat => - if (time::compare(current, stat.mtime) < 0) { - return true; + for (let dep .. deps) { + match (os::stat(dep)) { + case fs::error => + continue; + case let stat: fs::filestat => + if (time::compare(current, stat.mtime) < 0) { + return true; + }; }; }; return false; }; // Wrapper for [[fs::next]] that only returns valid submodule directories. -export fn next(it: *fs::iterator) (fs::dirent | void | fs::error) = { - for (true) match (fs::next(it)?) { - case void => - return void; - case let d: fs::dirent => +export fn next(it: *fs::iterator) (fs::dirent | done | fs::error) = { + for (let d => fs::next(it)?) { if (!fs::isdir(d.ftype)) { continue; }; @@ -57,18 +56,20 @@ export fn next(it: *fs::iterator) (fs::dirent | void | fs::error) = { return d; }; }; + return done; }; fn is_submodule(path: str) bool = { let it = strings::iter(path); - for (let first = true; true; first = false) match (strings::next(&it)) { - case void => - break; - case let r: rune => + + let first = true; + for (let r => strings::next(&it)) { if (!ascii::isalpha(r) && r != '_' && (first || !ascii::isdigit(r))) { return false; }; + + first = false; }; return true; }; diff --git a/hare/parse/+test/expr_test.ha b/hare/parse/+test/expr_test.ha @@ -199,7 +199,9 @@ }; @test fn for_expr() void = { - roundtrip("export fn main() void = { + roundtrip("fn next() (int | done) = 4; + +export fn main() void = { for (true) { x; }; @@ -215,7 +217,13 @@ for (let x = 10; x < 10; x) { x; }; - for (static let x = 0; x < 10) { + for (let x => next()) { + x; + }; + for (let x .. [1, 2, 3]) { + x; + }; + for (let x &.. [1, 2, 3]) { x; }; }; diff --git a/hare/parse/decl.ha b/hare/parse/decl.ha @@ -13,15 +13,12 @@ fn attr_symbol(lexer: *lex::lexer) (str | error) = { let s = t.1 as str; let d = strings::iter(s); match (strings::next(&d)) { - case void => void; + case done => void; case let r: rune => synassert(t.2, ascii::isalpha(r) || r == '.' || r == '_', "Invalid symbol")?; }; - for (true) match (strings::next(&d)) { - case void => - break; - case let r: rune => + for (let r => strings::next(&d)) { synassert(t.2, ascii::isalnum(r) || r == '$' || r == '.' || r == '_', "Invalid symbol")?; }; @@ -163,10 +160,9 @@ fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = { let tok = want(lexer, ltok::EQUAL, ltok::SEMICOLON)?; let body = switch (tok.0) { case ltok::EQUAL => - const params = prototype.params; - for (let i = 0z; i < len(params); i += 1) { - synassert(params[i].loc, - len(params[i].name) > 0, + for (let param &.. prototype.params) { + synassert(param.loc, + len(param.name) > 0, "Expected parameter name in function declaration")?; }; yield alloc(expr(lexer)?); diff --git a/hare/parse/doc/doc.ha b/hare/parse/doc/doc.ha @@ -63,10 +63,7 @@ fn _parse(sc: *bufio::scanner) (doc | ...error | utf8::invalid) = { let loc = lex::location { ... }; let doc: doc = []; - for (true) match (bufio::scan_rune(sc)?) { - case io::EOF => - break; - case let r: rune => + for (let r => bufio::scan_rune(sc)?) { if (r == ' ') { r = match (bufio::scan_rune(sc)?) { case io::EOF => @@ -101,10 +98,7 @@ fn scan_code_sample( loc: *lex::location, ) (code_sample | ...error | utf8::invalid) = { let s = memio::dynamic(); - for (true) match (bufio::scan_rune(sc)?) { - case io::EOF => - break; - case let r: rune => + for (let r => bufio::scan_rune(sc)?) { switch (r) { case '\t' => loc.col += 8 - loc.col % 8; @@ -197,10 +191,7 @@ fn scan_paragraph( defer io::close(&s)!; let state = state::NORMAL; - for (true) match (bufio::scan_rune(sc)?) { - case io::EOF => - break; - case let r: rune => + for (let r => bufio::scan_rune(sc)?) { switch (r) { case '\t' => if (state == state::NEWLINE && loc.col <= 1) { @@ -314,13 +305,13 @@ fn scan_paragraph( // Frees resources associated with a [[doc]]. export fn freeall(doc: doc) void = { - for (let i = 0z; i < len(doc); i += 1) { - match (doc[i]) { + for (let d .. doc) { + match (d) { case let p: paragraph => free_paragraph(p); case let l: list => - for (let i = 0z; i < len(l); i += 1) { - free_paragraph(l[i]); + for (let p .. l) { + free_paragraph(p); }; free(l); case let c: code_sample => @@ -331,8 +322,8 @@ export fn freeall(doc: doc) void = { }; fn free_paragraph(p: paragraph) void = { - for (let i = 0z; i < len(p); i += 1) { - match (p[i]) { + for (let entry .. p) { + match (entry) { case let s: str => free(s); case let d: decl_ref => diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha @@ -549,6 +549,8 @@ fn literal(lexer: *lex::lexer) (ast::expr | error) = { }; case ltok::VOID => yield void; + case ltok::DONE => + yield done; case ltok::TRUE => yield true; case ltok::FALSE => @@ -684,35 +686,104 @@ fn for_expr(lexer: *lex::lexer) (ast::expr | error) = { } else ""; want(lexer, ltok::LPAREN)?; - const bindings: nullable *ast::expr = - match (peek(lexer, ltok::DEF, ltok::LET, ltok::CONST, - ltok::STATIC)?) { - case void => - yield null; - case let tok: lex::token => - let is_static = if (tok.0 == ltok::STATIC) { - want(lexer, ltok::STATIC)?; - match (peek(lexer, ltok::LET, ltok::CONST)?) { - case lex::token => void; - case void => - want(lexer, ltok::LET, ltok::CONST)?; - abort(); // unreachable + let kind = void: (ast::for_kind | void); + let predicate_loc = lex::mkloc(lexer); + + const bindings = match (try(lexer, ltok::LET, ltok::CONST)?) { + case let tok: lex::token => + const binding_kind = switch (tok.0) { + case ltok::LET => + yield ast::binding_kind::LET; + case ltok::CONST => + yield ast::binding_kind::CONST; + case => abort(); // unreachable + }; + + let bindings: []ast::binding = []; + + for (true) { + const (tok, value, _) = want(lexer, + ltok::NAME, ltok::LPAREN)?; + const binding_name = switch (tok) { + case ltok::NAME => + yield value as str; + case ltok::LPAREN => + yield binding_unpack(lexer)?; + case => abort(); // unreachable + }; + const btype: nullable *ast::_type = + if (try(lexer, ltok::COLON)? is lex::token) { + yield alloc(_type(lexer)?); + } else null; + + const (tok, _, _) = want(lexer, ltok::EQUAL, + ltok::DOUBLE_DOT, ltok::BAND, ltok::ARROW)?; + + if (kind is void) { + switch (tok) { + case ltok::EQUAL => + kind = ast::for_kind::ACCUMULATOR; + case ltok::DOUBLE_DOT => + kind = ast::for_kind::EACH_VALUE; + case ltok::BAND => + want(lexer, ltok::DOUBLE_DOT)?; + kind = ast::for_kind::EACH_POINTER; + case ltok::ARROW => + kind = ast::for_kind::ITERATOR; + case => abort(); // unreachable }; - yield true; - } else false; - const bindings = alloc(binding(lexer, is_static)?); + } else if (kind as ast::for_kind != + ast::for_kind::ACCUMULATOR + || tok != ltok::EQUAL) { + return syntaxerr(lex::mkloc(lexer), + "Cannot create multiple bindings in non-c-style loop"); + }; + + const init_expr = alloc(expr(lexer)?); + + append(bindings, ast::binding { + name = binding_name, + _type = btype, + init = init_expr, + }); + + match (try(lexer, ltok::COMMA)?) { + case lex::token => + void; + case void => + break; + }; + }; + + if (kind as ast::for_kind == ast::for_kind::ACCUMULATOR) { want(lexer, ltok::SEMICOLON)?; - yield bindings; }; - const cond = alloc(expr(lexer)?); - const afterthought: nullable *ast::expr = - match (peek(lexer, ltok::SEMICOLON)?) { - case void => - yield null; + + yield alloc(ast::expr { + start = predicate_loc, + end = lex::prevloc(lexer), + expr = ast::binding_expr { + is_static = false, + kind = binding_kind, + bindings = bindings, + }, + }); + case void => + kind = ast::for_kind::ACCUMULATOR; + yield null; + }; + + const cond: nullable *ast::expr = null; + const afterthought: nullable *ast::expr = null; + + if (kind as ast::for_kind == ast::for_kind::ACCUMULATOR) { + cond = alloc(expr(lexer)?); + match (try(lexer, ltok::SEMICOLON)) { case lex::token => - want(lexer, ltok::SEMICOLON)?; - yield alloc(expr(lexer)?); + afterthought = alloc(expr(lexer)?); + case void => void; }; + }; want(lexer, ltok::RPAREN)?; @@ -721,6 +792,7 @@ fn for_expr(lexer: *lex::lexer) (ast::expr | error) = { start = tok.2, end = lex::prevloc(lexer), expr = ast::for_expr { + kind = kind as ast::for_kind, bindings = bindings, cond = cond, afterthought = afterthought, @@ -769,12 +841,12 @@ fn indexing(lexer: *lex::lexer, lvalue: ast::expr) (ast::expr | error) = { let is_slice = false; let start: nullable *ast::expr = null, end: nullable *ast::expr = null; - if (try(lexer, ltok::SLICE)? is lex::token) { + if (try(lexer, ltok::DOUBLE_DOT)? is lex::token) { is_slice = true; } else { start = alloc(expr(lexer)?); }; - if (!is_slice && try(lexer, ltok::SLICE)? is lex::token) { + if (!is_slice && try(lexer, ltok::DOUBLE_DOT)? is lex::token) { is_slice = true; }; if (is_slice && peek(lexer, ltok::RBRACKET)? is void) { @@ -820,7 +892,7 @@ fn plain_expression(lexer: *lex::lexer) (ast::expr | error) = { return literal(lexer); }; switch (tok.0) { - case ltok::TRUE, ltok::FALSE, ltok::NULL, ltok::VOID => + case ltok::TRUE, ltok::FALSE, ltok::NULL, ltok::VOID, ltok::DONE => return literal(lexer); case ltok::LBRACKET => return plain_array(lexer)?; diff --git a/hare/parse/type.ha b/hare/parse/type.ha @@ -114,6 +114,8 @@ fn primitive_type(lexer: *lex::lexer) (ast::_type | error) = { yield builtin_type::F64; case ltok::BOOL => yield builtin_type::BOOL; + case ltok::DONE => + yield builtin_type::DONE; case ltok::VALIST => yield builtin_type::VALIST; case ltok::VOID => @@ -480,12 +482,11 @@ export fn _type(lexer: *lex::lexer) (ast::_type | error) = { let tok = peek(lexer)? as lex::token; let typ: ast::_type = switch (tok.0) { - case ltok::RUNE, ltok::STR, ltok::BOOL, - ltok::I8, ltok::I16, ltok::I32, ltok::I64, - ltok::U8, ltok::U16, ltok::U32, ltok::U64, - ltok::INT, ltok::UINT, ltok::UINTPTR, ltok::SIZE, - ltok::F32, ltok::F64, - ltok::VALIST, ltok::VOID, ltok::OPAQUE, ltok::NEVER => + case ltok::RUNE, ltok::STR, ltok::BOOL, ltok::DONE, ltok::I8, ltok::I16, + ltok::I32, ltok::I64, ltok::U8, ltok::U16, ltok::U32, + ltok::U64, ltok::INT, ltok::UINT, ltok::UINTPTR, + ltok::SIZE, ltok::F32, ltok::F64, ltok::VALIST, + ltok::VOID, ltok::OPAQUE, ltok::NEVER => yield primitive_type(lexer)?; case ltok::ENUM => yield enum_type(lexer)?; diff --git a/hare/types/+test.ha b/hare/types/+test.ha @@ -330,9 +330,9 @@ fn resolve( assert(htype._align == 8); let t = htype.repr as tagged; assert(len(t) == 3); - assert(t[0].repr as builtin == builtin::STR); - assert(t[1].repr as builtin == builtin::INT); - assert(t[2].repr as builtin == builtin::VOID); + assert(t[0].repr as builtin == builtin::INT); + assert(t[1].repr as builtin == builtin::VOID); + assert(t[2].repr as builtin == builtin::STR); }; @test fn alias() void = { @@ -372,6 +372,7 @@ fn resolve( @test fn builtins() void = { const builtins = [ (&builtin_bool, "bool"), + (&builtin_done, "done"), (&builtin_f32, "f32"), (&builtin_f64, "f64"), (&builtin_i8, "i8"), diff --git a/hare/types/builtins.ha b/hare/types/builtins.ha @@ -12,11 +12,19 @@ export const builtin_bool: _type = _type { ... }; +// [[_type]] representation of done. +export const builtin_done: _type = _type { + repr = builtin::DONE, + sz = 0, _align = 0, + id = 3950255460, + ... +}; + // [[_type]] representation of f32. export const builtin_f32: _type = _type { repr = builtin::F32, sz = 4, _align = 4, - id = 3950255460, + id = 1568378015, ... }; @@ -24,7 +32,7 @@ export const builtin_f32: _type = _type { export const builtin_f64: _type = _type { repr = builtin::F64, sz = 8, _align = 8, - id = 1568378015, + id = 930681398, ... }; @@ -32,7 +40,7 @@ export const builtin_f64: _type = _type { export const builtin_i8: _type = _type { repr = builtin::I8, sz = 1, _align = 1, - id = 3312558843, + id = 2674862226, ... }; @@ -40,7 +48,7 @@ export const builtin_i8: _type = _type { export const builtin_i16: _type = _type { repr = builtin::I16, sz = 2, _align = 2, - id = 930681398, + id = 2037165609, ... }; @@ -48,7 +56,7 @@ export const builtin_i16: _type = _type { export const builtin_i32: _type = _type { repr = builtin::I32, sz = 4, _align = 4, - id = 2037165609, + id = 1399468992, ... }; @@ -56,7 +64,7 @@ export const builtin_i32: _type = _type { export const builtin_i64: _type = _type { repr = builtin::I64, sz = 8, _align = 8, - id = 1399468992, + id = 3312558843, ... }; @@ -64,7 +72,7 @@ export const builtin_i64: _type = _type { export const builtin_opaque: _type = _type { repr = builtin::OPAQUE, sz = SIZE_UNDEFINED, _align = SIZE_UNDEFINED, - id = 2374983655, + id = 1737287038, ... }; @@ -72,7 +80,7 @@ export const builtin_opaque: _type = _type { export const builtin_never: _type = _type { repr = builtin::NEVER, sz = SIZE_UNDEFINED, _align = SIZE_UNDEFINED, - id = 2374983655, + id = 1737287038, ... }; @@ -80,7 +88,7 @@ export const builtin_never: _type = _type { export const builtin_rune: _type = _type { repr = builtin::RUNE, sz = 4, _align = 4, - id = 1737287038, + id = 2843771249, ... }; @@ -88,7 +96,7 @@ export const builtin_rune: _type = _type { export const builtin_u8: _type = _type { repr = builtin::U8, sz = 1, _align = 1, - id = 1268499444, + id = 3181589295, ... }; @@ -96,7 +104,7 @@ export const builtin_u8: _type = _type { export const builtin_u16: _type = _type { repr = builtin::U16, sz = 2, _align = 2, - id = 4119164483, + id = 3481467866, ... }; @@ -104,7 +112,7 @@ export const builtin_u16: _type = _type { export const builtin_u32: _type = _type { repr = builtin::U32, sz = 4, _align = 4, - id = 3481467866, + id = 1906196061, ... }; @@ -112,7 +120,7 @@ export const builtin_u32: _type = _type { export const builtin_u64: _type = _type { repr = builtin::U64, sz = 8, _align = 8, - id = 1906196061, + id = 1268499444, ... }; @@ -120,6 +128,6 @@ export const builtin_u64: _type = _type { export const builtin_void: _type = _type { repr = builtin::VOID, sz = 0, _align = 0, - id = 3650376889, + id = 3012680272, ... }; diff --git a/hare/types/hash.ha b/hare/types/hash.ha @@ -8,15 +8,17 @@ use strings; // Keep ordered with respect to bootstrap harec:include/types.h type storage = enum u8 { - BOOL, F32, F64, I16, I32, I64, I8, INT, NEVER, NULL, OPAQUE, RUNE, SIZE, - STRING, U16, U32, U64, U8, UINT, UINTPTR, VOID, ALIAS, ARRAY, ENUM, - FUNCTION, POINTER, SLICE, STRUCT, TAGGED, TUPLE, UNION, VALIST, + BOOL, DONE, F32, F64, I16, I32, I64, I8, INT, NEVER, NULL, OPAQUE, RUNE, + SIZE, STRING, U16, U32, U64, U8, UINT, UINTPTR, VOID, ALIAS, ARRAY, + ENUM, FUNCTION, POINTER, SLICE, STRUCT, TAGGED, TUPLE, UNION, VALIST, }; fn builtin_storage(b: builtin) u8 = { switch (b) { case builtin::BOOL => return storage::BOOL; + case builtin::DONE => + return storage::DONE; case builtin::F32 => return storage::F32; case builtin::F64 => diff --git a/hare/types/store.ha b/hare/types/store.ha @@ -149,6 +149,8 @@ export fn lookup( return &builtin_u64; case builtin::VOID => return &builtin_void; + case builtin::DONE => + return &builtin_done; case => void; }; case => void; @@ -184,6 +186,9 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = { sz = store.arch._int; _align = store.arch._int; yield builtin::BOOL; + case ast::builtin_type::DONE => + sz = 0; _align = 0; + yield builtin::DONE; case ast::builtin_type::F32 => sz = 4; _align = 4; yield builtin::F32; diff --git a/hare/types/types.ha b/hare/types/types.ha @@ -21,9 +21,9 @@ export type array = struct { export type builtin = enum u8 { // Keep me consistent with ast::builtin - BOOL, F32, F64, FCONST, I16, I32, I64, I8, ICONST, INT, NEVER, NULL, - OPAQUE, RCONST, RUNE, SIZE, STR, U16, U32, U64, U8, UINT, UINTPTR, - VALIST, VOID, + BOOL, DONE, F32, F64, FCONST, I16, I32, I64, I8, ICONST, INT, NEVER, + NULL, OPAQUE, RCONST, RUNE, SIZE, STR, U16, U32, U64, U8, UINT, UINTPTR, + VALIST, VOID }; // An enum type, e.g. enum { FOO = 0 } diff --git a/hare/unparse/decl.ha b/hare/unparse/decl.ha @@ -162,11 +162,9 @@ fn comment(ctx: *context, syn: *synfunc, s: str) (size | io::error) = { let n = 0z; let s = strings::trimsuffix(s, "\n"); let s = strings::tokenize(s, "\n"); - for (true) match (strings::next_token(&s)) { - case void => - break; - case let line: str => - for (let j = 0z; j < ctx.indent; j += 1) { + + for (let line => strings::next_token(&s)) { + for (let i = 0z; i < ctx.indent; i += 1) { n += syn(ctx, "\t", synkind::COMMENT)?; ctx.linelen += 8; }; diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha @@ -203,61 +203,7 @@ fn _expr(ctx: *context, syn: *synfunc, e: *ast::expr) (size | io::error) = { z += binexprval(ctx, syn, e.rvalue, prec)?; return z; case let e: ast::binding_expr => - let z = 0z; - if (e.is_static) { - z += syn(ctx, "static", synkind::KEYWORD)?; - z += space(ctx)?; - }; - switch (e.kind) { - case ast::binding_kind::DEF => - z += syn(ctx, "def", synkind::KEYWORD)?; - case ast::binding_kind::CONST => - z += syn(ctx, "const", synkind::KEYWORD)?; - case ast::binding_kind::LET => - z += syn(ctx, "let", synkind::KEYWORD)?; - }; - z += space(ctx)?; - for (let i = 0z; i < len(e.bindings); i += 1) { - let binding = e.bindings[i]; - match (binding.name) { - case let s: str => - z += syn(ctx, s, synkind::IDENT)?; - case let u: ast::binding_unpack => - z += syn(ctx, "(", synkind::PUNCTUATION)?; - for (let i = 0z; i < len(u); i += 1) { - match (u[i]) { - case let s: str => - z += syn(ctx, s, - synkind::IDENT)?; - case void => - z += syn(ctx, "_", - synkind::OPERATOR)?; - }; - if (i + 1 < len(u)) { - z += syn(ctx, ",", - synkind::PUNCTUATION)?; - z += space(ctx)?; - }; - }; - z += syn(ctx, ")", synkind::PUNCTUATION)?; - }; - match (binding._type) { - case let t: *ast::_type => - z += syn(ctx, ":", synkind::PUNCTUATION)?; - z += space(ctx)?; - z += __type(ctx, syn, t)?; - case null => void; - }; - z += space(ctx)?; - z += syn(ctx, "=", synkind::OPERATOR)?; - z += space(ctx)?; - z += _expr(ctx, syn, binding.init)?; - if (i + 1 < len(e.bindings)) { - z += syn(ctx, ",", synkind::PUNCTUATION)?; - z += space(ctx)?; - }; - }; - return z; + return binding_expr(ctx, syn, &e, "=")?; case let e: ast::break_expr => let z = syn(ctx, "break", synkind::KEYWORD)?; if (e != "") { @@ -388,9 +334,9 @@ fn _expr(ctx: *context, syn: *synfunc, e: *ast::expr) (size | io::error) = { }; z += syn(ctx, "{", synkind::PUNCTUATION)?; ctx.indent += 1; - for (let i = 0z; i < len(e.exprs); i += 1) { + for (let expr .. e.exprs) { z += newline(ctx)?; - z += stmt(ctx, syn, e.exprs[i])?; + z += stmt(ctx, syn, expr)?; }; ctx.indent -= 1; z += newline(ctx)?; @@ -705,9 +651,9 @@ fn struct_literal( z += space(ctx)?; z += syn(ctx, "{", synkind::PUNCTUATION)?; ctx.indent += 1; - for (let i = 0z; i < len(sc.fields); i += 1) { + for (let field .. sc.fields) { z += newline(ctx)?; - match (sc.fields[i]) { + match (field) { case let sv: ast::struct_value => z += syn(ctx, sv.name, synkind::SECONDARY)?; match (sv._type) { @@ -736,6 +682,70 @@ fn struct_literal( return z; }; +fn binding_expr( + ctx: *context, + syn: *synfunc, + e: *ast::binding_expr, + assign_op: str +) (size | io::error) = { + let z = 0z; + if (e.is_static) { + z += syn(ctx, "static", synkind::KEYWORD)?; + z += space(ctx)?; + }; + switch (e.kind) { + case ast::binding_kind::DEF => + z += syn(ctx, "def", synkind::KEYWORD)?; + case ast::binding_kind::CONST => + z += syn(ctx, "const", synkind::KEYWORD)?; + case ast::binding_kind::LET => + z += syn(ctx, "let", synkind::KEYWORD)?; + }; + z += space(ctx)?; + for (let i = 0z; i < len(e.bindings); i += 1) { + let binding = e.bindings[i]; + + match (binding.name) { + case let s: str => + z += syn(ctx, s, synkind::IDENT)?; + case let u: ast::binding_unpack => + z += syn(ctx, "(", synkind::PUNCTUATION)?; + for (let i = 0z; i < len(u); i += 1) { + match (u[i]) { + case let s: str => + z += syn(ctx, s, + synkind::IDENT)?; + case void => + z += syn(ctx, "_", + synkind::OPERATOR)?; + }; + if (i + 1 < len(u)) { + z += syn(ctx, ",", + synkind::PUNCTUATION)?; + z += space(ctx)?; + }; + }; + z += syn(ctx, ")", synkind::PUNCTUATION)?; + }; + match (binding._type) { + case let t: *ast::_type => + z += syn(ctx, ":", synkind::PUNCTUATION)?; + z += space(ctx)?; + z += __type(ctx, syn, t)?; + case null => void; + }; + z += space(ctx)?; + z += syn(ctx, assign_op, synkind::OPERATOR)?; + z += space(ctx)?; + z += _expr(ctx, syn, binding.init)?; + if (i + 1 < len(e.bindings)) { + z += syn(ctx, ",", synkind::PUNCTUATION)?; + z += space(ctx)?; + }; + }; + return z; +}; + fn for_expr( ctx: *context, syn: *synfunc, @@ -749,22 +759,40 @@ fn for_expr( z += space(ctx)?; }; z += syn(ctx, "(", synkind::PUNCTUATION)?; + + let assign_op = switch (e.kind) { + case ast::for_kind::ACCUMULATOR => + yield "="; + case ast::for_kind::EACH_VALUE => + yield ".."; + case ast::for_kind::EACH_POINTER => + yield "&.."; + case ast::for_kind::ITERATOR => + yield "=>"; + }; + match (e.bindings) { + case let bind_expr: *ast::expr => + z += binding_expr(ctx, syn, + &(bind_expr.expr as ast::binding_expr), assign_op)?; + + if (e.kind == ast::for_kind::ACCUMULATOR) { + z += syn(ctx, ";", synkind::PUNCTUATION)?; + z += space(ctx)?; + }; case null => void; - case let e: *ast::expr => - z += _expr(ctx, syn, e)?; - z += syn(ctx, ";", synkind::PUNCTUATION)?; - z += space(ctx)?; }; - z += _expr(ctx, syn, e.cond)?; + if (e.kind == ast::for_kind::ACCUMULATOR) { + z += _expr(ctx, syn, e.cond as *ast::expr)?; - match (e.afterthought) { - case null => void; - case let e: *ast::expr => - z += syn(ctx, ";", synkind::PUNCTUATION)?; - z += space(ctx)?; - z += _expr(ctx, syn, e)?; + match (e.afterthought) { + case null => void; + case let e: *ast::expr => + z += syn(ctx, ";", synkind::PUNCTUATION)?; + z += space(ctx)?; + z += _expr(ctx, syn, e)?; + }; }; z += syn(ctx, ")", synkind::PUNCTUATION)?; @@ -791,9 +819,8 @@ fn switch_expr( z += space(ctx)?; z += syn(ctx, "{", synkind::PUNCTUATION)?; - for (let i = 0z; i < len(e.cases); i += 1) { + for (let item .. e.cases) { z += newline(ctx)?; - const item = e.cases[i]; z += syn(ctx, "case", synkind::KEYWORD)?; z += space(ctx)?; if (len(item.options) == 0) { @@ -837,10 +864,9 @@ fn match_expr( z += space(ctx)?; z += syn(ctx, "{", synkind::PUNCTUATION)?; - for (let i = 0z; i < len(e.cases); i += 1) { + for (let item .. e.cases) { z += newline(ctx)?; z += syn(ctx, "case", synkind::KEYWORD)?; - const item = e.cases[i]; if (len(item.name) > 0) { z += space(ctx)?; z += syn(ctx, "let", synkind::KEYWORD)?; @@ -900,9 +926,9 @@ fn case_exprs( case => void; }; ctx.indent += 1; - for (let j = 0z; j < len(exprs); j += 1) { + for (let expr .. exprs) { z += newline(ctx)?; - z += stmt(ctx, syn, exprs[j])?; + z += stmt(ctx, syn, expr)?; }; ctx.indent -= 1; diff --git a/hare/unparse/import.ha b/hare/unparse/import.ha @@ -76,11 +76,11 @@ export fn import( ... }, "use foo::bar::*;"), ]; - for (let i = 0z; i < len(tests); i += 1) { + for (let (ast_import, str_import) .. tests) { let buf = memio::dynamic(); - import(&buf, &syn_nowrap, &tests[i].0)!; + import(&buf, &syn_nowrap, &ast_import)!; let s = memio::string(&buf)!; - assert(s == tests[i].1); + assert(s == str_import); free(s); }; }; diff --git a/hare/unparse/type.ha b/hare/unparse/type.ha @@ -16,6 +16,8 @@ case ast::builtin_type::FCONST, ast::builtin_type::ICONST, abort("ICONST, FCONST, and RCONST have no lexical representation"); case ast::builtin_type::BOOL => yield "bool"; +case ast::builtin_type::DONE => + yield "done"; case ast::builtin_type::F32 => yield "f32"; case ast::builtin_type::F64 => @@ -116,18 +118,18 @@ fn struct_union_type( }; ctx.indent += 1z; - for (let i = 0z; i < len(membs); i += 1) { + for (let memb .. membs) { z += fmt::fprintln(ctx.out)?; ctx.linelen = 0; - if (membs[i].docs != "") { - z += comment(ctx, syn, membs[i].docs)?; + if (memb.docs != "") { + z += comment(ctx, syn, memb.docs)?; }; for (let i = 0z; i < ctx.indent; i += 1) { z += fmt::fprint(ctx.out, "\t")?; ctx.linelen += 8; }; - match (membs[i]._offset) { + match (memb._offset) { case null => void; case let ex: *ast::expr => z += syn(ctx, "@offset(", synkind::ATTRIBUTE)?; @@ -136,7 +138,7 @@ fn struct_union_type( z += space(ctx)?; }; - match (membs[i].member) { + match (memb.member) { case let se: ast::struct_embedded => z += __type(ctx, syn, se)?; case let sa: ast::struct_alias => @@ -216,8 +218,7 @@ fn __type(ctx: *context, syn: *synfunc, t: *ast::_type) (size | io::error) = { ctx.indent += 1; n += fmt::fprintln(ctx.out)?; ctx.linelen = 0; - for (let i = 0z; i < len(e.values); i += 1) { - let value = e.values[i]; + for (let value .. e.values) { let wrotedocs = false; if (value.docs != "") { // Check if comment should go above or next to diff --git a/hare/unparse/unit.ha b/hare/unparse/unit.ha @@ -12,8 +12,8 @@ export fn subunit( s: ast::subunit, ) (size | io::error) = { let n = 0z; - for (let i = 0z; i < len(s.imports); i += 1) { - n += import(out, syn, &s.imports[i])?; + for (let imp &.. s.imports) { + n += import(out, syn, imp)?; n += fmt::fprintln(out)?; }; if (len(s.imports) > 0) { diff --git a/io/copy.ha b/io/copy.ha @@ -51,16 +51,9 @@ fn copy_streams(dest: *stream, src: *stream) (size | error) = { fn copy_fallback(dest: handle, src: handle) (size | error) = { let w = 0z; let buf: [4096]u8 = [0...]; - for (true) { - match (read(src, buf[..])?) { - case let n: size => - if (n == 0) { - break; - }; - w += writeall(dest, buf[..n])?; - case EOF => - break; - }; + + for (let n => read(src, buf[..])?) { + w += writeall(dest, buf[..n])?; }; return w; }; diff --git a/io/drain.ha b/io/drain.ha @@ -6,13 +6,9 @@ export fn drain(in: handle) ([]u8 | error) = { let sink: []u8 = []; static let buf: [4096]u8 = [0...]; - for (true) { - match (read(in, buf[..])?) { - case let n: size => - append(sink, buf[..n]...); - case EOF => - break; - }; + + for (let n => read(in, buf[..])?) { + append(sink, buf[..n]...); }; return sink; }; diff --git a/io/types.ha b/io/types.ha @@ -11,7 +11,7 @@ export type underread = !size; export type error = !(...errors::error | underread); // Indicates an end-of-file condition. -export type EOF = void; +export type EOF = done; // Converts an I/O [[error]] into a user-friendly string. export fn strerror(err: error) str = { diff --git a/mime/database.ha b/mime/database.ha @@ -41,18 +41,18 @@ fn hashtable_insert(item: *mimetype) void = { let bucket = &mimetable[hash % len(mimetable)]; append(bucket, item); - for (let i = 0z; i < len(item.exts); i += 1) { - const hash = fnv::string(item.exts[i]); + for (let ext .. item.exts) { + const hash = fnv::string(ext); let bucket = &exttable[hash % len(exttable)]; append(bucket, item); }; }; @fini fn fini() void = { - for (let i = 0z; i < len(heap_db); i += 1) { - free(heap_db[i].mime); - strings::freeall(heap_db[i].exts); - free(heap_db[i]); + for (let m .. heap_db) { + free(m.mime); + strings::freeall(m.exts); + free(m); }; free(heap_db); free(static_db); diff --git a/mime/entries+test.ha b/mime/entries+test.ha @@ -23,8 +23,8 @@ const text_hare: mimetype = mimetype { assert(len(result.exts) >= 1); let extfound = false; - for (let i = 0z; i < len(result.exts); i += 1) { - if (result.exts[i] == "txt") { + for (let ext .. result.exts) { + if (ext == "txt") { extfound = true; }; }; @@ -35,8 +35,8 @@ const text_hare: mimetype = mimetype { assert(len(result.exts) >= 1); let extfound = false; - for (let i = 0z; i < len(result.exts); i += 1) { - if (result.exts[i] == "ha") { + for (let ext .. result.exts) { + if (ext == "ha") { extfound = true; }; }; @@ -52,8 +52,8 @@ const text_hare: mimetype = mimetype { assert(result.mime == "text/plain"); let extfound = false; - for (let i = 0z; i < len(result.exts); i += 1) { - if (result.exts[i] == "txt") { + for (let ext .. result.exts) { + if (ext == "txt") { extfound = true; }; }; @@ -67,8 +67,8 @@ const text_hare: mimetype = mimetype { assert(len(result.exts) >= 1); let extfound = false; - for (let i = 0z; i < len(result.exts); i += 1) { - if (result.exts[i] == "ha") { + for (let ext .. result.exts) { + if (ext == "ha") { extfound = true; }; }; diff --git a/mime/lookup.ha b/mime/lookup.ha @@ -9,8 +9,7 @@ use strings; export fn lookup_mime(mime: str) const nullable *mimetype = { const hash = fnv::string(mime); const bucket = &mimetable[hash % len(mimetable)]; - for (let i = 0z; i < len(bucket); i += 1) { - const item = bucket[i]; + for (let item .. *bucket) { if (item.mime == mime) { return item; }; @@ -24,8 +23,7 @@ export fn lookup_ext(ext: str) const nullable *mimetype = { ext = strings::ltrim(ext, '.'); const hash = fnv::string(ext); const bucket = &exttable[hash % len(exttable)]; - for (let i = 0z; i < len(bucket); i += 1) { - const item = bucket[i]; + for (let item .. *bucket) { for (let j = 0z; j < len(item.exts); j += 1) { if (item.exts[j] == ext) { return item; diff --git a/mime/parse.ha b/mime/parse.ha @@ -40,7 +40,7 @@ export fn next_param(in: *type_params) ((str, str) | void | errors::invalid) = { return errors::invalid; }; yield s; - case void => + case done => return; }; @@ -81,13 +81,7 @@ fn quoted(in: str) (str | errors::invalid) = { fn typevalid(in: str) (void | errors::invalid) = { const miter = strings::iter(in); - for (true) { - const rn = match (strings::next(&miter)) { - case let rn: rune => - yield rn; - case void => - break; - }; + for (let rn => strings::next(&miter)) { if (!ascii::valid(rn) || rn == ' ' || ascii::iscntrl(rn) || strings::contains(tspecial, rn)) { diff --git a/mime/system.ha b/mime/system.ha @@ -25,14 +25,7 @@ fn load_systemdb() (void | fs::error | io::error | utf8::invalid) = { let sc = bufio::newscanner(file, types::SIZE_MAX); defer bufio::finish(&sc); - for (true) { - let line = match (bufio::scan_line(&sc)?) { - case io::EOF => - break; - case let s: const str => - yield s; - }; - + for (let line => bufio::scan_line(&sc)?) { line = strings::trim(line); if (strings::hasprefix(line, "#") || len(line) == 0) { continue; @@ -50,13 +43,7 @@ fn load_systemdb() (void | fs::error | io::error | utf8::invalid) = { mime = strings::dup(mime), exts = [], }); - for (true) { - const ext = match (strings::next_token(&tok)) { - case let tok: str => - yield strings::trim(tok); - case void => - break; - }; + for (let ext => strings::next_token(&tok)) { append(entry.exts, strings::dup(ext)); }; register_heap(entry); diff --git a/net/ip/ip.ha b/net/ip/ip.ha @@ -76,7 +76,7 @@ export fn parsev4(st: str) (addr4 | invalid) = { return invalid; }; }; - if (i < 4 || !(strings::next_token(&tok) is void)) { + if (i < 4 || !(strings::next_token(&tok) is done)) { return invalid; }; return ret; @@ -102,7 +102,7 @@ export fn parsev6(st: str) (addr6 | invalid) = { let s = match (strings::next_token(&tok)) { case let s: str => yield s; - case void => + case done => break; }; if (s == "") { @@ -122,7 +122,7 @@ export fn parsev6(st: str) (addr6 | invalid) = { break; }; }; - if (!(strings::next_token(&tok) is void)) { + if (!(strings::next_token(&tok) is done)) { return invalid; }; if (ells >= 0) { @@ -255,7 +255,7 @@ export fn parsecidr(st: str) (subnet | invalid) = { case => return invalid; }; - if (!(strings::next_token(&tok) is void)) { + if (!(strings::next_token(&tok) is done)) { return invalid; }; return subnet { @@ -299,8 +299,8 @@ fn fmtmask(s: io::handle, mask: addr) (size | io::error) = { case void => // Format as hex, if zero runs are not contiguous // (like golang does) - for (let i = 0z; i < len(slice); i += 1) { - ret += fmt::fprintf(s, "{:x}", slice[i])?; + for (let part .. slice) { + ret += fmt::fprintf(s, "{:x}", part)?; }; case let n: size => // Standard CIDR integer @@ -344,7 +344,7 @@ fn wanttoken(tok: *strings::tokenizer) (str | invalid) = { match (strings::next_token(tok)) { case let s: str => return s; - case void => + case done => return invalid; }; }; @@ -382,6 +382,7 @@ export fn subnet_contains(sub: subnet, item: (addr | subnet)) bool = { // Mismatched addr4 and addr6 addresses / masks. return false; }; + for (let i = 0z; i < len(ipa); i += 1) { if (ipa[i] & maska[i] != ipb[i] & maska[i] || maska[i] > maskb[i]) { return false; diff --git a/net/ip/test+test.ha b/net/ip/test+test.ha @@ -55,8 +55,8 @@ fn ip_test(s: str, expected: (addr|invalid)) void = { ("a1:a2:a3:a4::b1:b2:b3:b4", invalid), ("", invalid), ]; - for (let i = 0z; i < len(tests); i += 1) { - ip_test(tests[i].0, tests[i].1); + for (let (string, expected) .. tests) { + ip_test(string, expected); }; }; @@ -83,8 +83,8 @@ fn subnet_test_simple(s: str) void = { "192.168.1.0/24", "192.168.1.0/32", ]; - for (let i = 0z; i < len(subnet_tests); i += 1) { - subnet_test_simple(subnet_tests[i]); + for (let test .. subnet_tests) { + subnet_test_simple(test); }; }; @@ -104,16 +104,14 @@ fn subnet_test_simple(s: str) void = { ("10.10.10.0/24", "10.10.10.0/23", false), ("10.10.10.0/24", "10.10.11.0/24", false), ]; - for (let i = 0z; i < len(addr_tests); i += 1) { - const input = addr_tests[i]; - let a = parsecidr(input.0)!; - let b = parse(input.1)!; - assert(subnet_contains(a, b) == input.2); + for (let (a, b, want) .. addr_tests) { + let a = parsecidr(a)!; + let b = parse(b)!; + assert(subnet_contains(a, b) == want); }; - for (let i = 0z; i < len(cidr_tests); i += 1) { - const input = cidr_tests[i]; - let a = parsecidr(input.0)!; - let b = parsecidr(input.1)!; - assert(subnet_contains(a, b) == input.2); + for (let (a, b, want) .. cidr_tests) { + let a = parsecidr(a)!; + let b = parsecidr(b)!; + assert(subnet_contains(a, b) == want); }; }; diff --git a/net/uri/fmt.ha b/net/uri/fmt.ha @@ -96,19 +96,13 @@ fn fmtaddr(out: io::handle, addr: ip::addr) (size | io::error) = { fn percent_encode(out: io::handle, src: str, allowed: str) (size | io::error) = { let iter = strings::iter(src); let n = 0z; - for (true) { - const r = match (strings::next(&iter)) { - case let r: rune => - yield r; - case => - break; - }; + for (let r => strings::next(&iter)) { if (ascii::isalnum(r) || strings::contains(allowed, r)) { n += fmt::fprint(out, r)?; } else { const en = utf8::encoderune(r); - for (let i = 0z; i < len(en); i += 1) { - n += fmt::fprintf(out, "%{:X}", en[i])?; + for (let elem .. en) { + n += fmt::fprintf(out, "%{:X}", elem)?; }; }; }; diff --git a/net/uri/parse.ha b/net/uri/parse.ha @@ -139,14 +139,7 @@ fn parse_authority( let userinfo = ""; let has_userinfo = false; - for (true) { - const r = match (strings::next(in)) { - case let r: rune => - yield r; - case void => - break; - }; - + for (let r => strings::next(in)) { if (r == '[') { if (len(memio::string(&buf)!) > 0) { if (len(userinfo) > 0) { @@ -239,25 +232,20 @@ fn parse_path(in: *strings::iterator, mode: path_mode) (str | invalid) = { if (!is_pchar(r)) { return invalid; }; - case void => + case done => break; }; }; }; - for (true) { - match (strings::next(in)) { - case let r: rune => - if (r == '?' || r == '#') { - strings::prev(in); - break; - }; - if (!is_pchar(r) && r != '/') { - return invalid; - }; - case void => + for (let r => strings::next(in)) { + if (r == '?' || r == '#') { + strings::prev(in); break; }; + if (!is_pchar(r) && r != '/') { + return invalid; + }; }; return percent_decode(strings::slice(&copy, in)); @@ -265,33 +253,23 @@ fn parse_path(in: *strings::iterator, mode: path_mode) (str | invalid) = { fn parse_query(in: *strings::iterator) (str | invalid) = { let copy = *in; - for (true) { - match (strings::next(in)) { - case let r: rune => - if (r == '#') { - strings::prev(in); - break; - }; - if (!is_pchar(r) && r != '/' && r != '?') { - return invalid; - }; - case void => + for (let r => strings::next(in)) { + if (r == '#') { + strings::prev(in); break; }; + if (!is_pchar(r) && r != '/' && r != '?') { + return invalid; + }; }; return strings::dup(strings::slice(&copy, in)); }; fn parse_fragment(in: *strings::iterator) (str | invalid) = { let copy = *in; - for (true) { - match (strings::next(in)) { - case let r: rune => - if (!is_pchar(r) && r != '/' && r != '?') { - return invalid; - }; - case void => - break; + for (let r => strings::next(in)) { + if (!is_pchar(r) && r != '/' && r != '?') { + return invalid; }; }; @@ -300,14 +278,7 @@ fn parse_fragment(in: *strings::iterator) (str | invalid) = { fn parse_port(in: *strings::iterator) (u16 | invalid) = { let copy = *in; - for (true) { - const r = match (strings::next(in)) { - case let r: rune => - yield r; - case void => - break; - }; - + for (let r => strings::next(in)) { if (!ascii::isdigit(r)) { strings::prev(in); break; @@ -368,7 +339,7 @@ fn percent_decode_static(out: io::handle, s: str) (void | invalid) = { memio::appendrune(out, r)!; }; - case void => + case done => if(len(percent_data) > 0) { match(strings::fromutf8(percent_data)) { case let stro: str => diff --git a/os/+freebsd/dirfdfs.ha b/os/+freebsd/dirfdfs.ha @@ -166,8 +166,8 @@ fn fs_open_file( flags: fs::flag... ) (io::file | fs::error) = { let oflags = fs::flag::RDONLY; - for (let i = 0z; i < len(flags); i += 1z) { - oflags |= flags[i]; + for (let flag .. flags) { + oflags |= flag; }; return _fs_open(fs, path, fsflags_to_bsd(oflags)?, 0); }; @@ -188,8 +188,8 @@ fn fs_create_file( if (len(flags) == 0z) { oflags |= fs::flag::WRONLY | fs::flag::TRUNC; }; - for (let i = 0z; i < len(flags); i += 1z) { - oflags |= flags[i]; + for (let flag .. flags) { + oflags |= flag; }; oflags |= fs::flag::CREATE; return _fs_open(fs, path, fsflags_to_bsd(oflags)?, mode)?; @@ -368,7 +368,7 @@ fn fs_iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = { return &iter.iter; }; -fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { +fn iter_next(iter: *fs::iterator) (fs::dirent | done | fs::error) = { let iter = iter: *os_iterator; if (iter.buf_pos >= iter.buf_end) { let n = match (rt::getdents(iter.fd, @@ -379,7 +379,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { yield n; }; if (n == 0) { - return; + return done; }; iter.buf_end = n; iter.buf_pos = 0; diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha @@ -49,11 +49,12 @@ type os_filesystem = struct { // Opens a file descriptor as an [[fs::fs]]. This file descriptor must be a // directory file. The file will be closed when the fs is closed. -export fn dirfdopen(fd: io::file, resolve: resolve_flag...) *fs::fs = { +export fn dirfdopen(fd: io::file, resolve_flags: resolve_flag...) *fs::fs = { let ofs = alloc(os_filesystem { ... }); let fs = static_dirfdopen(fd, ofs); - for (let i = 0z; i < len(resolve); i += 1) { - ofs.resolve |= resolve[i]; + + for (let flag .. resolve_flags) { + ofs.resolve |= flag; }; fs.close = &fs_close; return fs; @@ -90,12 +91,12 @@ fn static_dirfdopen(fd: io::file, filesystem: *os_filesystem) *fs::fs = { // Clones a dirfd filesystem, optionally adding additional [[resolve_flag]] // constraints. -export fn dirfs_clone(fs: *fs::fs, resolve: resolve_flag...) *fs::fs = { +export fn dirfs_clone(fs: *fs::fs, resolve_flags: resolve_flag...) *fs::fs = { assert(fs.open == &fs_open); let fs = fs: *os_filesystem; let new = alloc(*fs); - for (let i = 0z; i < len(resolve); i += 1) { - new.resolve |= resolve[i]; + for (let flag .. resolve_flags) { + fs.resolve |= flag; }; new.dirfd = rt::fcntl(new.dirfd, rt::F_DUPFD_CLOEXEC, 0) as int; return &new.fs; @@ -177,8 +178,8 @@ fn fs_open_file( flags: fs::flag... ) (io::file | fs::error) = { let oflags = fs::flag::RDONLY: int; - for (let i = 0z; i < len(flags); i += 1z) { - oflags |= flags[i]: int; + for (let flag .. flags) { + oflags |= flag: int; }; oflags ^= fs::flag::CTTY | fs::flag::NOCLOEXEC; // invert NOCTTY/CLOEXEC @@ -211,8 +212,8 @@ fn fs_create_file( if (len(flags) == 0) { oflags |= fs::flag::WRONLY | fs::flag::TRUNC; }; - for (let i = 0z; i < len(flags); i += 1z) { - oflags |= flags[i]: int; + for (let flag .. flags) { + oflags |= flag: int; }; oflags ^= fs::flag::CTTY | fs::flag::NOCLOEXEC; // invert NOCTTY/CLOEXEC oflags |= fs::flag::CREATE: int; @@ -431,7 +432,7 @@ fn fs_iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = { return &iter.iter; }; -fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { +fn iter_next(iter: *fs::iterator) (fs::dirent | done | fs::error) = { let iter = iter: *os_iterator; if (iter.buf_pos >= iter.buf_end) { let n = match (rt::getdents64(iter.fd, @@ -442,7 +443,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { yield n; }; if (n == 0) { - return; + return done; }; iter.buf_end = n; iter.buf_pos = 0; diff --git a/os/+openbsd/dirfdfs.ha b/os/+openbsd/dirfdfs.ha @@ -90,8 +90,8 @@ fn fs_open_file( flags: fs::flag... ) (io::file | fs::error) = { let oflags = fs::flag::RDONLY; - for (let i = 0z; i < len(flags); i += 1z) { - oflags |= flags[i]; + for (let flag .. flags) { + oflags |= flag; }; return _fs_open(fs, path, fsflags_to_bsd(oflags)?, 0); }; @@ -129,8 +129,8 @@ fn fs_create_file( if (len(flags) == 0z) { oflags |= fs::flag::WRONLY | fs::flag::TRUNC; }; - for (let i = 0z; i < len(flags); i += 1z) { - oflags |= flags[i]; + for (let flag .. flags) { + oflags |= flag; }; oflags |= fs::flag::CREATE; return _fs_open(fs, path, fsflags_to_bsd(oflags)?, mode)?; @@ -169,7 +169,7 @@ type os_iterator = struct { buf: []u8, }; -fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { +fn iter_next(iter: *fs::iterator) (fs::dirent | done | fs::error) = { let iter = iter: *os_iterator; for (true) { @@ -182,7 +182,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { yield n; }; if (n == 0) { - return; + return done; }; iter.buf_end = n; iter.buf_pos = 0; @@ -190,6 +190,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { let de = &iter.buf[iter.buf_pos]: *rt::dirent; iter.buf_pos += de.d_reclen: int; + // getdents() may return invalid entries which will have // d_fileno set to 0 if (de.d_fileno == 0) { diff --git a/os/environ.ha b/os/environ.ha @@ -31,8 +31,8 @@ let envp: []str = []; // Looks up an environment variable and returns its value, or void if unset. export fn getenv(name: const str) (str | void) = { getenvs(); // populate envp - for (let i = 0z; i < len(envp); i += 1) { - let (key, value) = strings::cut(envp[i], "="); + for (let ent .. envp) { + let (key, value) = strings::cut(ent, "="); if (key == name) return value; }; }; diff --git a/os/exec/+freebsd/exec.ha b/os/exec/+freebsd/exec.ha @@ -96,24 +96,24 @@ fn platform_exec(cmd: *command) error = { // We don't worry about freeing the return values from c::fromstr // because once we exec(2) our heap is fried anyway let argv: []nullable *const c::char = alloc([], len(cmd.argv) + 1z); - for (let i = 0z; i < len(cmd.argv); i += 1z) { - append(argv, c::fromstr(cmd.argv[i])); + for (let arg .. cmd.argv) { + append(argv, c::fromstr(arg)); }; append(argv, null); let envp: nullable *[*]nullable *const c::char = null; if (len(cmd.env) != 0) { let env: []nullable *const c::char = alloc([], len(cmd.env) + 1); - for (let i = 0z; i < len(cmd.env); i += 1) { - append(env, c::fromstr(cmd.env[i])); + for (let e .. cmd.env) { + append(env, c::fromstr(e)); }; append(env, null); envp = env: *[*]nullable *const c::char; }; let need_devnull = false; - for (let i = 0z; i < len(cmd.files); i += 1) { - const from = match (cmd.files[i].0) { + for (let file &.. cmd.files) { + const from = match (file.0) { case let file: io::file => yield file; case nullfd => @@ -123,7 +123,7 @@ fn platform_exec(cmd: *command) error = { continue; }; - cmd.files[i].0 = match (rt::fcntl(from, rt::F_DUPFD_CLOEXEC, 0)) { + file.0 = match (rt::fcntl(from, rt::F_DUPFD_CLOEXEC, 0)) { case let fd: int => yield fd; case let err: rt::errno => @@ -135,18 +135,18 @@ fn platform_exec(cmd: *command) error = { yield os::open("/dev/null")!; } else -1; - for (let i = 0z; i < len(cmd.files); i += 1) { - const from = match (cmd.files[i].0) { + for (let file .. cmd.files) { + const from = match (file.0) { case let file: io::file => yield file; case nullfd => yield devnull; case closefd => - io::close(cmd.files[i].1)?; + io::close(file.1)?; continue; }; - if (cmd.files[i].1 == from) { + if (file.1 == from) { let flags = match (rt::fcntl(from, rt::F_GETFD, 0)) { case let flags: int => yield flags; @@ -155,7 +155,7 @@ fn platform_exec(cmd: *command) error = { }; rt::fcntl(from, rt::F_SETFD, flags & ~rt::FD_CLOEXEC)!; } else { - match (rt::dup2(from, cmd.files[i].1)) { + match (rt::dup2(from, file.1)) { case int => void; case let e: rt::errno => return errors::errno(e); diff --git a/os/exec/+linux/exec.ha b/os/exec/+linux/exec.ha @@ -97,24 +97,24 @@ fn platform_exec(cmd: *command) error = { // We don't worry about freeing the return values from c::fromstr // because once we exec(2) our heap is fried anyway let argv: []nullable *const c::char = alloc([], len(cmd.argv) + 1z); - for (let i = 0z; i < len(cmd.argv); i += 1z) { - append(argv, c::fromstr(cmd.argv[i])); + for (let arg .. cmd.argv) { + append(argv, c::fromstr(arg)); }; append(argv, null); let envp: nullable *[*]nullable *const c::char = null; if (len(cmd.env) != 0) { let env: []nullable *const c::char = alloc([], len(cmd.env) + 1); - for (let i = 0z; i < len(cmd.env); i += 1) { - append(env, c::fromstr(cmd.env[i])); + for (let e .. cmd.env) { + append(env, c::fromstr(e)); }; append(env, null); envp = env: *[*]nullable *const c::char; }; let need_devnull = false; - for (let i = 0z; i < len(cmd.files); i += 1) { - const from = match (cmd.files[i].0) { + for (let file &.. cmd.files) { + const from = match (file.0) { case let file: io::file => yield file; case nullfd => @@ -124,7 +124,7 @@ fn platform_exec(cmd: *command) error = { continue; }; - cmd.files[i].0 = match (rt::fcntl(from, rt::F_DUPFD_CLOEXEC, 0)) { + file.0 = match (rt::fcntl(from, rt::F_DUPFD_CLOEXEC, 0)) { case let fd: int => yield fd; case let err: rt::errno => @@ -136,18 +136,18 @@ fn platform_exec(cmd: *command) error = { yield os::open("/dev/null")!; } else -1; - for (let i = 0z; i < len(cmd.files); i += 1) { - const from = match (cmd.files[i].0) { + for (let file .. cmd.files) { + const from = match (file.0) { case let file: io::file => yield file; case nullfd => yield devnull; case closefd => - io::close(cmd.files[i].1)?; + io::close(file.1)?; continue; }; - if (cmd.files[i].1 == from) { + if (file.1 == from) { let flags = match (rt::fcntl(from, rt::F_GETFD, 0)) { case let flags: int => yield flags; @@ -156,7 +156,7 @@ fn platform_exec(cmd: *command) error = { }; rt::fcntl(from, rt::F_SETFD, flags & ~rt::FD_CLOEXEC)!; } else { - match (rt::dup2(from, cmd.files[i].1)) { + match (rt::dup2(from, file.1)) { case int => void; case let e: rt::errno => return errors::errno(e); diff --git a/os/exec/+openbsd/exec.ha b/os/exec/+openbsd/exec.ha @@ -71,24 +71,24 @@ fn platform_exec(cmd: *command) error = { // We don't worry about freeing the return values from c::fromstr // because once we exec(2) our heap is fried anyway let argv: []nullable *const c::char = alloc([], len(cmd.argv) + 1z); - for (let i = 0z; i < len(cmd.argv); i += 1z) { - append(argv, c::fromstr(cmd.argv[i])); + for (let arg .. cmd.argv) { + append(argv, c::fromstr(arg)); }; append(argv, null); let envp: nullable *[*]nullable *const c::char = null; if (len(cmd.env) != 0) { let env: []nullable *const c::char = alloc([], len(cmd.env) + 1); - for (let i = 0z; i < len(cmd.env); i += 1) { - append(env, c::fromstr(cmd.env[i])); + for (let e .. cmd.env) { + append(env, c::fromstr(e)); }; append(env, null); envp = env: *[*]nullable *const c::char; }; let need_devnull = false; - for (let i = 0z; i < len(cmd.files); i += 1) { - const from = match (cmd.files[i].0) { + for (let file &.. cmd.files) { + const from = match (file.0) { case let file: io::file => yield file; case nullfd => @@ -98,7 +98,7 @@ fn platform_exec(cmd: *command) error = { continue; }; - cmd.files[i].0 = match (rt::fcntl(from, rt::F_DUPFD_CLOEXEC, 0)) { + file.0 = match (rt::fcntl(from, rt::F_DUPFD_CLOEXEC, 0)) { case let fd: int => yield fd; case let err: rt::errno => @@ -110,18 +110,18 @@ fn platform_exec(cmd: *command) error = { yield os::open("/dev/null")!; } else -1; - for (let i = 0z; i < len(cmd.files); i += 1) { - const from = match (cmd.files[i].0) { + for (let file .. cmd.files) { + const from = match (file.0) { case let file: io::file => yield file; case nullfd => yield devnull; case closefd => - io::close(cmd.files[i].1)?; + io::close(file.1)?; continue; }; - if (cmd.files[i].1 == from) { + if (file.1 == from) { let flags = match (rt::fcntl(from, rt::F_GETFD, 0)) { case let flags: int => yield flags; @@ -130,7 +130,7 @@ fn platform_exec(cmd: *command) error = { }; rt::fcntl(from, rt::F_SETFD, flags & ~rt::FD_CLOEXEC)!; } else { - match (rt::dup2(from, cmd.files[i].1)) { + match (rt::dup2(from, file.1)) { case int => void; case let e: rt::errno => return errors::errno(e); diff --git a/os/exec/cmd.ha b/os/exec/cmd.ha @@ -188,14 +188,9 @@ fn lookup_open(name: str) (platform_cmd | void | error) = { }; let tok = strings::tokenize(path, ":"); - for (true) { - const item = match (strings::next_token(&tok)) { - case void => - break; - case let s: str => - yield s; - }; + for (let item => strings::next_token(&tok)) { path::set(&buf, item, name)!; + match (open(path::string(&buf))) { case (errors::noaccess | errors::noentry) => continue; @@ -235,14 +230,9 @@ export fn lookup(name: str) (str | void) = { }; let tok = strings::tokenize(path, ":"); - for (true) { - const item = match (strings::next_token(&tok)) { - case void => - break; - case let s: str => - yield s; - }; + for (let item => strings::next_token(&tok)) { path::set(&buf, item, name)!; + match (os::access(path::string(&buf), os::amode::X_OK)) { case let exec: bool => if (exec) { diff --git a/path/buffer.ha b/path/buffer.ha @@ -52,12 +52,13 @@ export fn local(path: str) str = { fn _local(path: str, buf: *[MAX]u8) str = { let buf = buf[..0]; - const path = strings::toutf8(path); - for (let i = 0z; i < len(path); i += 1) { - if (path[i] == '/') { + const bytes = strings::toutf8(path); + + for (let byte .. bytes) { + if (byte == '/') { static append(buf, SEP); } else { - static append(buf, path[i]); + static append(buf, byte); }; }; return strings::fromutf8(buf)!; diff --git a/path/ext_stack.ha b/path/ext_stack.ha @@ -7,16 +7,16 @@ use strings; // Add extensions onto the end of the final path segment. The separating '.' // will be inserted for you. If the final path segment consists entirely of dots // or the path is root, this function will return [[cant_extend]]. -export fn push_ext(buf: *buffer, ext: str...) (str | error) = { +export fn push_ext(buf: *buffer, exts: str...) (str | error) = { match (peek(buf)) { case void => return cant_extend; case let s: str => if (strings::ltrim(s, '.') == "") return cant_extend; }; - for (let i = 0z; i < len(ext); i += 1) { - const newend = buf.end + 1 + len(ext[i]); + for (let ext .. exts) { + const newend = buf.end + 1 + len(ext); if (MAX < newend) return too_long; buf.buf[buf.end] = '.'; - buf.buf[buf.end+1..newend] = strings::toutf8(ext[i]); + buf.buf[buf.end+1..newend] = strings::toutf8(ext); buf.end = newend; }; return string(buf); diff --git a/path/iter.ha b/path/iter.ha @@ -34,10 +34,10 @@ export fn peekiter(it: *iterator) (str | void) = { return strings::fromutf8_unsafe(result); }; -// Returns the next path component from an [[iterator]], or void if none +// Returns the next path component from an [[iterator]], or done if none // remain. Advances the iterator. -export fn nextiter(it: *iterator) (str | void) = { - if (len(it.cur) == 0) return void; +export fn nextiter(it: *iterator) (str | done) = { + if (len(it.cur) == 0) return done; const (result, remaining) = split_iter(it); it.cur = remaining; return strings::fromutf8_unsafe(result); @@ -80,39 +80,39 @@ export fn iterrem(it: *iterator) str = strings::fromutf8_unsafe(it.cur); assert(nextiter(&i) as str == "foo"); assert(nextiter(&i) as str == "bar"); assert(nextiter(&i) as str == "baz"); - assert(nextiter(&i) is void); + assert(nextiter(&i) is done); i = riter(&buf); assert(nextiter(&i) as str == "baz"); assert(nextiter(&i) as str == "bar"); assert(nextiter(&i) as str == "foo"); assert(nextiter(&i) as str == local("/")); - assert(nextiter(&i) is void); + assert(nextiter(&i) is done); set(&buf, local("foo/bar/baz"))!; i = iter(&buf); assert(nextiter(&i) as str == "foo"); assert(nextiter(&i) as str == "bar"); assert(nextiter(&i) as str == "baz"); - assert(nextiter(&i) is void); + assert(nextiter(&i) is done); i = riter(&buf); assert(nextiter(&i) as str == "baz"); assert(nextiter(&i) as str == "bar"); assert(nextiter(&i) as str == "foo"); - assert(nextiter(&i) is void); + assert(nextiter(&i) is done); set(&buf, "foo")!; i = iter(&buf); assert(nextiter(&i) as str == "foo"); - assert(nextiter(&i) is void); + assert(nextiter(&i) is done); i = riter(&buf); assert(nextiter(&i) as str == "foo"); - assert(nextiter(&i) is void); + assert(nextiter(&i) is done); set(&buf, local("/"))!; i = iter(&buf); assert(nextiter(&i) as str == local("/")); - assert(nextiter(&i) is void); + assert(nextiter(&i) is done); i = riter(&buf); assert(nextiter(&i) as str == local("/")); - assert(nextiter(&i) is void); + assert(nextiter(&i) is done); }; diff --git a/path/stack.ha b/path/stack.ha @@ -7,8 +7,9 @@ use strings; // Appends path elements onto the end of a path buffer. // Returns the new string value of the path. export fn push(buf: *buffer, items: str...) (str | error) = { - for (let i = 0z; i < len(items); i += 1) { - let elem = strings::toutf8(items[i]); + for (let item .. items) { + let elem = strings::toutf8(item); + for (true) match (bytes::index(elem, SEP)) { case void => buf.end = appendnorm(buf, elem)?; diff --git a/regex/+test.ha b/regex/+test.ha @@ -629,18 +629,12 @@ fn run_rawreplace_case( // (`^([ab]*?)(?<!(a))c`, "abc", matchres::MATCH, 0, -1), ]; - for (let i = 0z; i < len(cases); i += 1) { - const expr = cases[i].0; - const string = cases[i].1; - const should_match = cases[i].2; - const start = cases[i].3; - const end = if (cases[i].4 == -1) { + for (let (expr, string, should_match, start, end) .. cases) { + if (end == -1) { // workaround to get the length in codepoints let runes = strings::torunes(string); defer free(runes); - yield len(runes): int; - } else { - yield cases[i].4; + end = len(runes): int; }; run_find_case(expr, string, should_match, start, end); }; @@ -651,11 +645,7 @@ fn run_rawreplace_case( ["aaa bbb ccc", "bbb", "ccc"]: []str), ]; - for (let i = 0z; i < len(submatch_cases); i += 1) { - const expr = submatch_cases[i].0; - const string = submatch_cases[i].1; - const should_match = submatch_cases[i].2; - const targets = submatch_cases[i].3; + for (let (expr, string, should_match, targets) .. submatch_cases) { run_submatch_case(expr, string, should_match, targets); }; }; @@ -674,11 +664,7 @@ fn run_rawreplace_case( ["aaa", ""]: []str), ]; - for (let i = 0z; i < len(cases); i += 1) { - const expr = cases[i].0; - const string = cases[i].1; - const should_match = cases[i].2; - const targets = cases[i].3; + for (let (expr, string, should_match, targets) .. cases) { run_findall_case(expr, string, should_match, targets); }; }; @@ -702,12 +688,7 @@ fn run_rawreplace_case( (`([[:digit:]])([[:digit:]])`, "1234", `\2`, 1, "234"), ]; - for (let i = 0z; i < len(cases); i += 1) { - const expr = cases[i].0; - const string = cases[i].1; - const target = cases[i].2; - const n = cases[i].3; - const expected = cases[i].4; + for (let (expr, string, target, n, expected) .. cases) { run_replace_case(expr, string, target, n, expected); }; }; @@ -727,12 +708,7 @@ fn run_rawreplace_case( (`.`, "blablabla", `x`, 0, "blablabla"), ]; - for (let i = 0z; i < len(cases); i += 1) { - const expr = cases[i].0; - const string = cases[i].1; - const target = cases[i].2; - const n = cases[i].3; - const expected = cases[i].4; + for (let (expr, string, target, n, expected) .. cases) { run_rawreplace_case(expr, string, target, n, expected); }; }; diff --git a/regex/regex.ha b/regex/regex.ha @@ -101,8 +101,8 @@ export type regex = struct { // Frees resources associated with a [[regex]]. export fn finish(re: *regex) void = { free(re.insts); - for (let i = 0z; i < len(re.charsets); i += 1) { - free(re.charsets[i]); + for (let charset .. re.charsets) { + free(charset); }; free(re.charsets); }; @@ -130,13 +130,13 @@ fn handle_bracket( const peek1 = strings::next(iter); const peek2 = strings::next(iter); const peek3 = strings::next(iter); - if (!(peek1 is void)) { + if (!(peek1 is done)) { strings::prev(iter); }; - if (!(peek2 is void)) { + if (!(peek2 is done)) { strings::prev(iter); }; - if (!(peek3 is void)) { + if (!(peek3 is done)) { strings::prev(iter); }; @@ -154,14 +154,14 @@ fn handle_bracket( }; const is_range = peek1 is rune && peek1 as rune == '-' - && !(peek2 is void) && !(peek3 is void) + && !(peek2 is done) && !(peek3 is done) && !(peek2 as rune == ']'); const range_end = peek2; const is_first_char = *bracket_idx == 0 || *bracket_idx == 1 && !*is_charset_positive; if (r == '\\') { - if (peek1 is void) { + if (peek1 is done) { return `Trailing backslash '\'`: error; } else { append(charsets[len(charsets) - 1], @@ -180,7 +180,7 @@ fn handle_bracket( *is_charset_positive = true; } else if (r == '^' && *bracket_idx == 0) { *is_charset_positive = false; - } else if (r == '[' && !(peek1 is void) + } else if (r == '[' && !(peek1 is done) && peek1 as rune == ':') { const rest = strings::iterstr(iter); const n_cc = len(charclass_map); @@ -239,7 +239,7 @@ export fn compile(expr: str) (regex | error) = { }; if (in_bracket) { - if (next is void) { + if (next is done) { return `Unmatched '['`: error; }; const r = next: rune; @@ -251,7 +251,7 @@ export fn compile(expr: str) (regex | error) = { }; const r = match (next) { - case void => + case done => if (n_groupstarts > 0) { return `Unmatched '('`: error; }; @@ -261,7 +261,7 @@ export fn compile(expr: str) (regex | error) = { switch (r) { case '\\' => const peek1 = strings::next(&iter); - if (peek1 is void) { + if (peek1 is done) { return `Trailing backslash '\'`: error; } else { append(insts, (peek1 as rune): inst_lit); @@ -477,10 +477,9 @@ fn is_consuming_inst(a: inst) bool = { fn add_thread(threads: *[]thread, parent_idx: size, new_pc: size) void = { // Do not add this thread if there is already another thread with // the same PC - for (let i = 0z; i < len(threads); i += 1) { - if (threads[i].pc == new_pc - && !threads[i].matched - && threads[i].start_idx + for (let thread &.. *threads) { + if (thread.pc == new_pc && !thread.matched + && thread.start_idx < threads[parent_idx].start_idx) { return; }; @@ -730,10 +729,9 @@ fn search( // When we only want the leftmost match, delete all threads that // start after the earliest non-zero-length matched thread if (first_match_idx is size) { - for (let i = 0z; i < len(threads); i += 1) { - if (threads[i].start_idx - > first_match_idx as size) { - threads[i].failed = true; + for (let thread &.. threads) { + if (thread.start_idx > first_match_idx as size) { + thread.failed = true; }; }; }; @@ -896,7 +894,7 @@ fn parse_replace_target(targetstr: str) ([]([]u8 | size) | error) = { let iter = strings::iter(targetstr); let start = 0z, end = 0z; for (true) match (strings::next(&iter)) { - case void => + case done => if (start != end) { append(target, bytes[start..]); }; @@ -908,7 +906,7 @@ fn parse_replace_target(targetstr: str) ([]([]u8 | size) | error) = { }; const r = match (strings::next(&iter)) { - case void => + case done => return "Trailing backslash": error; case let r: rune => yield r; @@ -978,8 +976,8 @@ export fn result_free(s: result) void = { // Frees a slice of [[result]]s. export fn result_freeall(s: []result) void = { - for (let i = 0z; i < len(s); i += 1) { - result_free(s[i]); + for (let res .. s) { + result_free(res); }; free(s); }; diff --git a/shlex/escape.ha b/shlex/escape.ha @@ -9,15 +9,7 @@ use strings; fn is_safe(s: str) bool = { const iter = strings::iter(s); - for (true) { - const rn = match (strings::next(&iter)) { - case let rn: rune => - yield rn; - case void => - break; - }; - - + for (let rn => strings::next(&iter)) { switch (rn) { case '@', '%', '+', '=', ':', ',', '.', '/', '-' => void; @@ -42,14 +34,7 @@ export fn quote(sink: io::handle, s: str) (size | io::error) = { let z = io::writeall(sink, ['\''])?; const iter = strings::iter(s); - for (true) { - const rn = match (strings::next(&iter)) { - case let rn: rune => - yield rn; - case void => - break; - }; - + for (let rn => strings::next(&iter)) { if (rn == '\'') { z += io::writeall(sink, strings::toutf8(`'"'"'`))?; } else { diff --git a/shlex/split.ha b/shlex/split.ha @@ -23,25 +23,15 @@ export fn split(in: const str) ([]str | syntaxerr) = { let first = true; let dirty = false; - for (true) { - const r = match (strings::next(&iter)) { - case let r: rune => - yield r; - case void => - break; - }; - + for (let r => strings::next(&iter)) { dirty = true; switch (r) { case ' ', '\t', '\n' => - for (true) match (strings::next(&iter)) { - case let r: rune => + for (let r => strings::next(&iter)) { if (r != ' ' && r != '\t' && r != '\n') { strings::prev(&iter); // Unget break; }; - case void => - break; }; if (!first) { const item = memio::string(&s)!; @@ -76,7 +66,7 @@ fn scan_backslash(out: io::handle, in: *strings::iterator) (void | syntaxerr) = const r = match (strings::next(in)) { case let r: rune => yield r; - case void => + case done => return syntaxerr; }; @@ -96,7 +86,7 @@ fn scan_double(out: io::handle, in: *strings::iterator) (void | syntaxerr) = { const r = match (strings::next(in)) { case let r: rune => yield r; - case void => + case done => return syntaxerr; }; @@ -116,7 +106,7 @@ fn scan_single(out: io::handle, in: *strings::iterator) (void | syntaxerr) = { const r = match (strings::next(in)) { case let r: rune => yield r; - case void => + case done => return syntaxerr; }; diff --git a/strings/concat.ha b/strings/concat.ha @@ -4,12 +4,13 @@ // Concatenates multiple strings. The caller must free the return value. export fn concat(strs: str...) str = { let z = 0z; - for (let i = 0z; i < len(strs); i += 1) { - z += len(strs[i]); + for (let s .. strs) { + z += len(s); }; + let new: []u8 = alloc([], z); - for (let i = 0z; i < len(strs); i += 1) { - static append(new, toutf8(strs[i])...); + for (let s .. strs) { + static append(new, toutf8(s)...); }; return fromutf8_unsafe(new); }; diff --git a/strings/contains.ha b/strings/contains.ha @@ -7,8 +7,8 @@ use encoding::utf8; // Returns true if a string contains a rune or a sub-string, multiple of which // can be given. export fn contains(haystack: str, needles: (str | rune)...) bool = { - for (let i = 0z; i < len(needles); i += 1) { - const matched = match (needles[i]) { + for (let needle .. needles) { + const matched = match (needle) { case let s: str => yield bytes::contains(toutf8(haystack), toutf8(s)); diff --git a/strings/dup.ha b/strings/dup.ha @@ -23,10 +23,10 @@ export fn dup(s: const str) str = { // Creates a copy of a []str slice with all the strings duplicated. The result // must be freed using [[freeall]]. -export fn dupall(s: []str) []str = { - let newsl: []str = alloc([], len(s)); - for (let i = 0z; i < len(s); i += 1) { - static append(newsl, dup(s[i])); +export fn dupall(strs: []str) []str = { + let newsl: []str = alloc([], len(strs)); + for (let s .. strs) { + static append(newsl, dup(s)); }; return newsl; }; diff --git a/strings/index.ha b/strings/index.ha @@ -36,7 +36,7 @@ fn index_rune(s: str, r: rune) (size | void) = { if (r == n) { return i; }; - case void => + case done => break; }; }; @@ -50,7 +50,7 @@ fn rindex_rune(s: str, r: rune) (size | void) = { if (r == n) { return i; }; - case void => + case done => break; }; }; @@ -64,17 +64,17 @@ fn index_string(s: str, needle: str) (size | void) = { for (true) { const rest_rune = next(&rest_iter); const needle_rune = next(&needle_iter); - if (rest_rune is void && !(needle_rune is void)) { + if (rest_rune is done && !(needle_rune is done)) { break; }; - if (needle_rune is void) { + if (needle_rune is done) { return i; }; if ((rest_rune as rune) != (needle_rune as rune)) { break; }; }; - if (next(&s_iter) is void) { + if (next(&s_iter) is done) { break; }; }; @@ -88,17 +88,17 @@ fn rindex_string(s: str, needle: str) (size | void) = { for (true) { const rest_rune = next(&rest_iter); const needle_rune = next(&needle_iter); - if (rest_rune is void && !(needle_rune is void)) { + if (rest_rune is done && !(needle_rune is done)) { break; }; - if (needle_rune is void) { + if (needle_rune is done) { return i - len(needle); }; if ((rest_rune as rune) != (needle_rune as rune)) { break; }; }; - if (next(&s_iter) is void) { + if (next(&s_iter) is done) { break; }; }; diff --git a/strings/iter.ha b/strings/iter.ha @@ -37,23 +37,23 @@ export fn riter(src: str) iterator = { return ret; }; -// Get the next rune from an iterator, or void if there are none left. +// Get the next rune from an iterator, or done if there are none left. // // Be aware that a rune is not the minimum lexographical unit of language in // Unicode strings. If you use these runes to construct a new string, // reordering, editing, or omitting any of the runes without careful discretion // may cause linguistic errors to arise. To avoid this, you may need to use a // third-party Unicode module instead. -export fn next(iter: *iterator) (rune | void) = move(!iter.reverse, iter); +export fn next(iter: *iterator) (rune | done) = move(!iter.reverse, iter); -// Get the previous rune from an iterator, or void when at the start of the +// Get the previous rune from an iterator, or done when at the start of the // string. -export fn prev(iter: *iterator) (rune | void) = move(iter.reverse, iter); +export fn prev(iter: *iterator) (rune | done) = move(iter.reverse, iter); -fn move(forward: bool, iter: *iterator) (rune | void) = { +fn move(forward: bool, iter: *iterator) (rune | done) = { let fun = if (forward) &utf8::next else &utf8::prev; return match (fun(&iter.dec)) { - case void => void; + case void => yield done; case (utf8::more | utf8::invalid) => abort("Invalid UTF-8 string (this should not happen)"); case let r: rune => @@ -83,7 +83,7 @@ export fn slice(begin: *iterator, end: *iterator) str = { @test fn iter() void = { let s = iter("こんにちは"); - assert(prev(&s) is void); + assert(prev(&s) is done); const expected1 = ['こ', 'ん']; for (let i = 0z; i < len(expected1); i += 1) { assert(next(&s) as rune == expected1[i]); @@ -94,8 +94,8 @@ export fn slice(begin: *iterator, end: *iterator) str = { for (let i = 0z; i < len(expected2); i += 1) { assert(next(&s) as rune == expected2[i]); }; - assert(next(&s) is void); - assert(next(&s) is void); + assert(next(&s) is done); + assert(next(&s) is done); assert(prev(&s) as rune == 'は'); s = riter("にちは"); @@ -103,7 +103,7 @@ export fn slice(begin: *iterator, end: *iterator) str = { for (let i = 0z; i < len(expected3); i += 1) { assert(next(&s) as rune == expected3[i]); }; - assert(next(&s) is void); + assert(next(&s) is done); assert(prev(&s) as rune == 'に'); }; diff --git a/strings/replace.ha b/strings/replace.ha @@ -24,8 +24,8 @@ export fn multireplace(s: str, repls: (str, str)...) str = { let i = 0z; let prev = 0z; // end of previous match, so we can append in chunks for :step (i < len(b)) { - for (let j = 0z; j < len(repls); j += 1) { - const replb = (toutf8(repls[j].0), toutf8(repls[j].1)); + for (let (replace, with) .. repls) { + const replb = (toutf8(replace), toutf8(with)); if (bytes::hasprefix(b[i..], replb.0)) { append(res, b[prev..i]...); append(res, replb.1...); diff --git a/strings/runes.ha b/strings/runes.ha @@ -8,21 +8,17 @@ use encoding::utf8; export fn torunes(s: str) []rune = { let sl: []rune = []; let iter = iter(s); - for (true) { - match (next(&iter)) { - case void => break; - case let r: rune => - append(sl, r); - }; + for (let r => next(&iter)) { + append(sl, r); }; return sl; }; // Returns a string from a slice of runes. The caller must free the return value. -export fn fromrunes(rs: []rune) str = { +export fn fromrunes(runes: []rune) str = { let bytes: []u8 = []; - for (let i = 0z; i < len(rs); i += 1) { - const bs = utf8::encoderune(rs[i]); + for (let r .. runes) { + const bs = utf8::encoderune(r); append(bytes, bs...); }; return fromutf8_unsafe(bytes); @@ -41,15 +37,17 @@ export fn fromrunes(rs: []rune) str = { ("こんにちは世界!", ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!']), ]; - for (let i = 0z; i < len(tests); i += 1) { - const s = fromrunes(tests[i].1); + for (let (string, runes) .. tests) { + const s = fromrunes(runes); defer free(s); - assert(s == tests[i].0); + assert(s == string); + const rs = torunes(s); defer free(rs); - assert(len(rs) == len(tests[i].1)); + assert(len(rs) == len(runes)); + for (let j = 0z; j < len(rs); j += 1) { - assert(rs[j] == tests[i].1[j]); + assert(rs[j] == runes[j]); }; }; }; diff --git a/strings/sub.ha b/strings/sub.ha @@ -10,7 +10,7 @@ fn utf8_byte_len_bounded(iter: *iterator, end: size) size = { match (next(iter)) { case let r: rune => continue; - case void => + case done => abort("index exceeds string length"); }; }; diff --git a/strings/template/template.ha b/strings/template/template.ha @@ -30,14 +30,7 @@ export fn compile(input: str) (template | invalid) = { let instrs: []instruction = []; const iter = strings::iter(input); - for (true) { - const rn = match (strings::next(&iter)) { - case void => - break; - case let rn: rune => - yield rn; - }; - + for (let rn => strings::next(&iter)) { if (rn == '$') { match (strings::next(&iter)) { case let next_rn: rune => @@ -69,8 +62,8 @@ export fn compile(input: str) (template | invalid) = { // Frees resources associated with a [[template]]. export fn finish(tmpl: *template) void = { - for (let i = 0z; i < len(tmpl); i += 1) { - match (tmpl[i]) { + for (let instr .. *tmpl) { + match (instr) { case let lit: literal => free(lit); case let var: variable => @@ -89,8 +82,8 @@ export fn execute( params: param... ) (size | io::error) = { let z = 0z; - for (let i = 0z; i < len(tmpl); i += 1) { - match (tmpl[i]) { + for (let instr .. *tmpl) { + match (instr) { case let lit: literal => z += fmt::fprint(out, lit)?; case let var: variable => @@ -103,9 +96,9 @@ export fn execute( fn get_param(name: str, params: param...) fmt::formattable = { // TODO: Consider preparing a parameter map or something - for (let i = 0z; i < len(params); i += 1) { - if (params[i].0 == name) { - return params[i].1; + for (let (var_name, obj) .. params) { + if (var_name == name) { + return obj; }; }; fmt::errorfln("strings::template: required parameter ${} was not provided", name)!; diff --git a/strings/tokenize.ha b/strings/tokenize.ha @@ -35,13 +35,13 @@ export fn rtokenize(s: str, delim: str) tokenizer = bytes::rtokenize(toutf8(s), toutf8(delim)); // Returns the next string from a tokenizer, and advances the cursor. Returns -// void if there are no tokens left. -export fn next_token(s: *tokenizer) (str | void) = { +// done if there are no tokens left. +export fn next_token(s: *tokenizer) (str | done) = { let s = s: *bytes::tokenizer; return match (bytes::next_token(s)) { case let b: []u8 => yield fromutf8_unsafe(b); - case void => void; + case void => yield done; }; }; @@ -78,11 +78,11 @@ export fn remaining_tokens(s: *tokenizer) str = { assert(peek_token(&tok) as str == ""); assert(next_token(&tok) as str == ""); assert(peek_token(&tok) is void); - assert(next_token(&tok) is void); + assert(next_token(&tok) is done); let tok = tokenize("", "foo"); assert(peek_token(&tok) is void); - assert(next_token(&tok) is void); + assert(next_token(&tok) is done); let tok = rtokenize("Hello, my name is drew", " "); assert(next_token(&tok) as str == "drew"); @@ -105,7 +105,7 @@ export fn splitn(in: str, delim: str, n: size) []str = { match (next_token(&tok)) { case let s: str => append(toks, s); - case void => + case done => return toks; }; }; @@ -129,7 +129,7 @@ export fn rsplitn(in: str, delim: str, n: size) []str = { match (next_token(&tok)) { case let s: str => append(toks, s); - case void => + case done => return toks; }; }; diff --git a/strings/trim.ha b/strings/trim.ha @@ -14,15 +14,9 @@ export fn ltrim(input: str, trim: rune...) str = { return fromutf8_unsafe(bytes::ltrim(input, whitespace...)); }; let it = iter(input); - for :outer (true) { - const r = match (next(&it)) { - case let r: rune => - yield r; - case void => - break; - }; - for (let i = 0z; i < len(trim); i += 1) { - if (r == trim[i]) { + for :outer (let r => next(&it)) { + for (let tr .. trim) { + if (r == tr) { continue :outer; }; }; @@ -41,15 +35,9 @@ export fn rtrim(input: str, trim: rune...) str = { return fromutf8_unsafe(bytes::rtrim(input, whitespace...)); }; let it = riter(input); - for :outer (true) { - const r = match (next(&it)) { - case let r: rune => - yield r; - case void => - break; - }; - for (let i = 0z; i < len(trim); i += 1) { - if (r == trim[i]) { + for :outer (let r => next(&it)) { + for (let tr .. trim) { + if (r == tr) { continue :outer; }; }; diff --git a/test/+test.ha b/test/+test.ha @@ -67,8 +67,8 @@ fn finish_context(ctx: *context) void = { io::close(&ctx.stderr)!; free(ctx.failures); free(ctx.skipped); - for (let i = 0z; i < len(ctx.output); i += 1) { - finish_output(&ctx.output[i]); + for (let out &.. ctx.output) { + finish_output(out); }; free(ctx.output); free(ctx.cwd); @@ -92,8 +92,8 @@ export @symbol("__test_main") fn main() size = { if (len(os::args) == 1) { append(enabled_tests, tests...); } else for (let i = 0z; i < ntest; i += 1) { - for (let j = 1z; j < len(os::args); j += 1) { - if (fnmatch::fnmatch(os::args[j], tests[i].name)) { + for (let arg .. os::args) { + if (fnmatch::fnmatch(arg, tests[i].name)) { append(enabled_tests, tests[i]); break; }; @@ -105,9 +105,9 @@ export @symbol("__test_main") fn main() size = { }; let maxname = 0z; - for (let i = 0z; i < len(enabled_tests); i += 1) { - if (len(enabled_tests[i].name) > maxname) { - maxname = len(enabled_tests[i].name); + for (let test .. enabled_tests) { + if (len(test.name) > maxname) { + maxname = len(test.name); }; }; @@ -123,14 +123,13 @@ export @symbol("__test_main") fn main() size = { fmt::printfln("Running {}/{} tests:\n", len(enabled_tests), ntest)!; reset(&ctx); - for (let i = 0z; i < len(enabled_tests); i += 1) { - do_test(&ctx, enabled_tests[i]); + for (let test .. enabled_tests) { + do_test(&ctx, test); }; fmt::println()!; - for (let i = 0z; i < len(ctx.skipped); i += 1) { - fmt::printfln("Skipped {}: {}", ctx.skipped[i].test, - ctx.skipped[i].reason)!; + for (let skipped .. ctx.skipped) { + fmt::printfln("Skipped {}: {}", skipped.test, skipped.reason)!; }; if (len(ctx.skipped) > 0) { fmt::println()!; @@ -138,19 +137,19 @@ export @symbol("__test_main") fn main() size = { if (len(ctx.failures) > 0) { fmt::println("Failures:")!; - for (let i = 0z; i < len(ctx.failures); i += 1) { - match (ctx.failures[i].reason.path) { + for (let failure .. ctx.failures) { + match (failure.reason.path) { case null => fmt::printfln("{}: {}", - ctx.failures[i].test, - ctx.failures[i].reason.msg)!; + failure.test, + failure.reason.msg)!; case let path: *str => fmt::printfln("{}: {}:{}:{}: {}", - ctx.failures[i].test, + failure.test, *path, - ctx.failures[i].reason.line, - ctx.failures[i].reason.col, - ctx.failures[i].reason.msg)!; + failure.reason.line, + failure.reason.col, + failure.reason.msg)!; }; }; fmt::println()!; @@ -256,7 +255,7 @@ fn printable(buf: []u8) str = { case let s: str => let it = strings::iter(s); for (true) match (strings::next(&it)) { - case void => + case done => return strings::dup(s); case let r: rune => if (ascii::valid(r) && !ascii::isprint(r) diff --git a/test/util+test.ha b/test/util+test.ha @@ -33,8 +33,7 @@ export fn skip(reason: str) never = { // keywords. If all the keywords are present, return void. Otherwise, skip the // currently running test. export fn require(keywords: str...) void = { - for :keywords (let i = 0z; i < len(keywords); i += 1) { - let keyword = keywords[i]; + for :keywords (let keyword .. keywords) { let tokr = strings::tokenize(os::tryenv("HARETEST_INCLUDE", ""), " "); for (true) { match (strings::next_token(&tokr)) { @@ -42,7 +41,7 @@ export fn require(keywords: str...) void = { if (tok == keyword) { continue :keywords; }; - case void => + case done => skip(fmt::asprintf( "Requires HARETEST_INCLUDE='{}'", strings::join(" ", keywords...), diff --git a/time/chrono/leapsec.ha b/time/chrono/leapsec.ha @@ -56,15 +56,8 @@ fn init_utc_leapsecs() (void | utciniterror) = { // Parse UTC/TAI leap second data from [[UTC_LEAPSECS_PATH]]. // See file for format details. fn parse_utc_leapsecs(h: io::handle) (void | utf8::invalid | io::error) = { - for (true) { - const line = match (bufio::read_line(h)) { - case let err: io::error => - return err; - case io::EOF => - return; - case let line: []u8 => - yield strings::fromutf8(line)?; - }; + for (let line => bufio::read_line(h)?) { + let line = strings::fromutf8(line)?; defer free(line); if (strings::hasprefix(line, '#')) { @@ -87,13 +80,7 @@ fn parse_utc_leapsecs(h: io::handle) (void | utf8::invalid | io::error) = { fn scan_number(iter: *strings::iterator) (i64 | void) = { let begin = *iter; - for (true) { - const rn = match (strings::next(iter)) { - case void => - break; - case let rn: rune => - yield rn; - }; + for (let rn => strings::next(iter)) { switch (rn) { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => continue; @@ -107,13 +94,7 @@ fn scan_number(iter: *strings::iterator) (i64 | void) = { }; fn scan_whitespace(iter: *strings::iterator) void = { - for (true) { - const rn = match (strings::next(iter)) { - case void => - return; - case let rn: rune => - yield rn; - }; + for (let rn => strings::next(iter)) { switch (rn) { case ' ', '\t' => continue; diff --git a/time/chrono/timescale.ha b/time/chrono/timescale.ha @@ -58,9 +58,10 @@ export fn convert(i: time::instant, tscs: *timescale...) (time::instant | analyt // default to TAI intermediary const convs = a.convto(&tai, t) as []time::instant; - for (let l = 0z; l < len(convs); l += 1) { + + for (let conv .. convs) { append(tmps, ( - b.convfrom(&tai, convs[l]) as []time::instant + b.convfrom(&tai, conv) as []time::instant )...); }; }; @@ -506,8 +507,7 @@ fn mtc_convfrom(ts: *timescale, i: time::instant) ([]time::instant | void) = { (( 1483228802i64, 0i64), [( 1483228839i64, 0i64)]), ]; - for (let idx = 0z; idx < len(testcases); idx += 1) { - let testcase = testcases[idx]; + for (let testcase .. testcases) { let params = testcase.0; let param = time::instant{ sec = params.0, nsec = params.1 }; let expect = testcase.1; @@ -602,8 +602,7 @@ fn mtc_convfrom(ts: *timescale, i: time::instant) ([]time::instant | void) = { (( 1483228839i64, 0i64), [( 1483228802i64, 0i64)]), ]; - for (let idx = 0z; idx < len(testcases); idx += 1) { - let testcase = testcases[idx]; + for (let testcase .. testcases) { let params = testcase.0; let param = time::instant{ sec = params.0, nsec = params.1 }; let expect = testcase.1; diff --git a/time/chrono/timezone.ha b/time/chrono/timezone.ha @@ -74,8 +74,8 @@ type tzname = struct { // Frees a [[timezone]]. A [[locality]] argument can be passed. export fn timezone_free(tz: *timezone) void = { free(tz.name); - for (let i = 0z; i < len(tz.zones); i += 1) { - zone_finish(&tz.zones[i]); + for (let zone &.. tz.zones) { + zone_finish(zone); }; free(tz.zones); free(tz.transitions); diff --git a/time/date/date.ha b/time/date/date.ha @@ -275,8 +275,7 @@ export fn from_str( ]; let buf: [64]u8 = [0...]; - for (let i = 0z; i < len(testcases); i += 1) { - const t = testcases[i]; + for (let t .. testcases) { const expect = t.3; const actual = from_str(t.0, t.1, t.2...); diff --git a/time/date/daydate.ha b/time/date/daydate.ha @@ -282,10 +282,7 @@ fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { (( 1970, 1, 99), 0, true), (( 1970, 99, 99), 0, true), ]; - for (let i = 0z; i < len(cases); i += 1) { - const params = cases[i].0; - const expect = cases[i].1; - const should_error = cases[i].2; + for (let (params, expect, should_error) .. cases) { const actual = calc_daydate__ymd( params.0, params.1, params.2, ); @@ -334,9 +331,7 @@ fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { ((29349, 3, 6), 9999999), ]; - for (let i = 0z; i < len(cases); i += 1) { - const ywd = cases[i].0; - const expected = cases[i].1; + for (let (ywd, expected) .. cases) { const actual = calc_daydate__ywd(ywd.0, ywd.1, ywd.2)!; assert(actual == expected, "incorrect calc_daydate__ywd() result"); @@ -368,10 +363,7 @@ fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { (29349, 25, 9999999), ]; - for (let i = 0z; i < len(cases); i += 1) { - const y = cases[i].0; - const yd = cases[i].1; - const expected = cases[i].2; + for (let (y, yd, expected) .. cases) { const actual = calc_daydate__yd(y, yd)!; assert(expected == actual, "error in date calculation from yd"); @@ -406,9 +398,7 @@ fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { (1000000, ( 4707, 11, 29)), (9999999, (29349, 1, 25)), ]; - for (let i = 0z; i < len(cases); i += 1) { - const paramt = cases[i].0; - const expect = cases[i].1; + for (let (paramt, expect) .. cases) { const actual = calc_ymd(paramt); assert(expect.0 == actual.0, "year mismatch"); assert(expect.1 == actual.1, "month mismatch"); @@ -441,9 +431,7 @@ fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { (( 4707, 11, 29), 333), ((29349, 1, 25), 25), ]; - for (let i = 0z; i < len(cases); i += 1) { - const params = cases[i].0; - const expect = cases[i].1; + for (let (params, expect) .. cases) { const actual = calc_yearday(params.0, params.1, params.2); assert(expect == actual, "yearday miscalculation"); }; @@ -468,9 +456,7 @@ fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { ((366, 0), 53), ]; - for (let i = 0z; i < len(cases); i += 1) { - const params = cases[i].0; - const expect = cases[i].1; + for (let (params, expect) .. cases) { const actual = calc_week(params.0, params.1); assert(expect == actual, "week miscalculation"); }; @@ -495,9 +481,7 @@ fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { ((366, 0), 53), ]; - for (let i = 0z; i < len(cases); i += 1) { - const params = cases[i].0; - const expect = cases[i].1; + for (let (params, expect) .. cases) { const actual = calc_sundayweek(params.0, params.1); assert(expect == actual, "week miscalculation"); }; @@ -527,9 +511,7 @@ fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { (1000000, 4), // 4707-11-29 (9999999, 5), // 29349-01-25 ]; - for (let i = 0z; i < len(cases); i += 1) { - const paramt = cases[i].0; - const expect = cases[i].1; + for (let (paramt, expect) .. cases) { const actual = calc_weekday(paramt); assert(expect == actual, "weekday miscalculation"); }; @@ -610,9 +592,7 @@ fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { (2038, 4), (2039, 5), ]; - for (let i = 0z; i < len(cases); i += 1) { - const paramt = cases[i].0; - const expect = cases[i].1; + for (let (paramt, expect) .. cases) { const actual = calc_janfirstweekday(paramt); assert(expect == actual, "calc_janfirstweekday() miscalculation"); }; diff --git a/time/date/format.ha b/time/date/format.ha @@ -231,14 +231,7 @@ export fn format( const iter = strings::iter(layout); let escaped = false; let n = 0z; - for (true) { - let r: rune = match (strings::next(&iter)) { - case void => - break; - case let r: rune => - yield r; - }; - + for (let r => strings::next(&iter)) { if (escaped) { escaped = false; n += fmtout(h, r, d)?; @@ -299,9 +292,7 @@ export fn format( ("%s", "757390625"), ]; - for (let i = 0z; i < len(cases); i += 1) { - const layout = cases[i].0; - const expected = cases[i].1; + for (let (layout, expected) .. cases) { const actual = asformat(layout, &d)!; defer free(actual); if (actual != expected) { diff --git a/time/date/parithm.ha b/time/date/parithm.ha @@ -284,10 +284,7 @@ export fn truncate(d: date, u: unit) date = { }, ), ]; - for (let i = 0z; i < len(cases); i += 1) { - const da = cases[i].0; - const db = cases[i].1; - const expected = cases[i].2; + for (let (da, db, expected) .. cases) { const actual = pdiff(da, db); assert(peq(actual, expected), "pdiff miscalculation"); }; @@ -322,10 +319,7 @@ export fn truncate(d: date, u: unit) date = { time::SECOND)), ), ]; - for (let i = 0z; i < len(cases); i += 1) { - const da = cases[i].0; - const db = cases[i].1; - const expected = cases[i].2; + for (let (da, db, expected) .. cases) { assert(unitdiff(da, db, unit::YEAR) == expected.0, "invalid diff_in_years() result"); assert(unitdiff(da, db, unit::MONTH) == expected.1, diff --git a/time/date/parse.ha b/time/date/parse.ha @@ -29,14 +29,7 @@ export fn parse(v: *virtual, layout: str, s: str) (void | parsefail) = { const siter = strings::iter(s); let escaped = false; - for (true) { - const lr: rune = match (strings::next(&liter)) { - case void => - break; - case let lr: rune => - yield lr; - }; - + for (let lr => strings::next(&liter)) { if (!escaped && lr == '%') { escaped = true; continue; @@ -44,7 +37,7 @@ export fn parse(v: *virtual, layout: str, s: str) (void | parsefail) = { if (!escaped) { const sr = match (strings::next(&siter)) { - case void => + case done => return (liter.dec.offs, lr); case let sr: rune => yield sr; @@ -140,7 +133,7 @@ fn parse_specifier( fn eat_rune(iter: *strings::iterator, needle: rune) (uint | failure) = { const rn = match (strings::next(iter)) { - case void => + case done => return failure; case let rn: rune => yield rn; @@ -180,7 +173,7 @@ fn scan_int(iter: *strings::iterator, maxrunes: size) (int | failure) = { let startfixed = false; for (let i = 0z; i < maxrunes; i += 1) { let rn: rune = match (strings::next(iter)) { - case void => + case done => break; case let rn: rune => yield rn; @@ -211,7 +204,7 @@ fn scan_num(iter: *strings::iterator, maxrunes: size) (i64 | failure) = { let start = *iter; for (let i = 0z; i < maxrunes; i += 1) { match (strings::next(iter)) { - case void => + case done => return failure; case let rn: rune => if (!ascii::isdigit(rn)) { @@ -236,7 +229,7 @@ fn scan_decimal(iter: *strings::iterator, maxrunes: size) (i64 | failure) = { let start = *iter; for (let i = 0z; i < maxrunes; i += 1) { let rn: rune = match (strings::next(iter)) { - case void => + case done => break; case let rn: rune => yield rn; @@ -269,7 +262,7 @@ fn scan_decimal(iter: *strings::iterator, maxrunes: size) (i64 | failure) = { // fn scan_zo(iter: *strings::iterator) (time::duration | failure) = { const first = match (strings::next(iter)) { - case void => + case done => return failure; case let first: rune => yield first; @@ -281,7 +274,7 @@ fn scan_zo(iter: *strings::iterator) (time::duration | failure) = { let zo = scan_int(iter, 2)? * time::HOUR; match (strings::next(iter)) { - case void => + case done => return failure; case let sep: rune => if (sep != ':') { @@ -301,15 +294,10 @@ fn scan_zo(iter: *strings::iterator) (time::duration | failure) = { // Scans and parses locality names, made of printable characters. fn scan_str(iter: *strings::iterator) (str | failure) = { let start = *iter; - for (true) { - match (strings::next(iter)) { - case void => + for (let rn => strings::next(iter)) { + if (!ascii::isgraph(rn)) { + strings::prev(iter); break; - case let rn: rune => - if (!ascii::isgraph(rn)) { - strings::prev(iter); - break; - }; }; }; return strings::slice(&start, iter); diff --git a/time/date/period.ha b/time/date/period.ha @@ -30,18 +30,18 @@ export fn peq(pa: period, pb: period) bool = { // Returns the sum [[period]] of a set of periods. export fn sum(ps: period...) period = { - let p = period { ... }; - for (let i = 0z; i < len(ps); i += 1) { - p.years += ps[i].years; - p.months += ps[i].months; - p.weeks += ps[i].weeks; - p.days += ps[i].days; - p.hours += ps[i].hours; - p.minutes += ps[i].minutes; - p.seconds += ps[i].seconds; - p.nanoseconds += ps[i].nanoseconds; + let out = period { ... }; + for (let p &.. ps) { + out.years += p.years; + out.months += p.months; + out.weeks += p.weeks; + out.days += p.days; + out.hours += p.hours; + out.minutes += p.minutes; + out.seconds += p.seconds; + out.nanoseconds += p.nanoseconds; }; - return p; + return out; }; // Returns a [[period]] with its fields negated. diff --git a/time/date/reckon.ha b/time/date/reckon.ha @@ -83,8 +83,7 @@ export fn reckon(d: date, calc: calculus, ps: period...) date = { calc |= calculus::CEIL; }; - for (let i = 0z; i < len(ps); i += 1) if (calc & calculus::REVSIG == 0) { - const p = ps[i]; + for (let p .. ps) if (calc & calculus::REVSIG == 0) { const fold = calculus::FOLD; r.year = r.year as int + p.years: int; @@ -103,7 +102,6 @@ export fn reckon(d: date, calc: calculus, ps: period...) date = { reckon_seconds(&r, p.seconds, fold); reckon_nanoseconds(&r, p.nanoseconds, fold); } else { - const p = ps[i]; const fold = calculus::FOLD | calculus::REVSIG; reckon_nanoseconds(&r, p.nanoseconds, fold); diff --git a/time/date/virtual.ha b/time/date/virtual.ha @@ -159,8 +159,7 @@ export fn realize( v.loc = v.vloc as chrono::locality; } else if (v.locname is str) { v.loc = chrono::UTC; - for (let i = 0z; i < len(locs); i += 1) { - const loc = locs[i]; + for (let loc .. locs) { if (loc.name == v.locname as str) { v.loc = loc; break;