hare

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

commit 34fdb97585fee2ce307b1190d8e5dd0a8ccf8936
parent 0332d9d04bc654e6d74ef2550a751d8e4b18e547
Author: Drew DeVault <sir@cmpwn.com>
Date:   Thu, 16 Sep 2021 17:55:09 +0200

all: overhaul switch/match syntax

This changes the syntax of switch and match expressions following
similar changes to harec et al.

match (x) {
	case type =>
		do_work();
		yield 10;
	case x: type =>
		process(x);
		yield 20;
	case =>
		abort();
};

Signed-off-by: Drew DeVault <sir@cmpwn.com>
Signed-off-by: Alexey Yerin <yyp@disroot.org>
Co-authored-by: Alexey Yerin <yyp@disroot.org>

Diffstat:
Mascii/strcmp.ha | 40++++++++++++++++++++++++++--------------
Mbufio/buffered.ha | 23+++++++++++++----------
Mbufio/memstream.ha | 33+++++++++++++++------------------
Mbufio/scanner.ha | 64++++++++++++++++++++++++++++++++++++++--------------------------
Mbytes/contains.ha | 6++++--
Mbytes/index.ha | 25++++++++++++++++---------
Mbytes/tokenize.ha | 93+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mcmd/hare/main.ha | 25+++++++++++++++----------
Mcmd/hare/plan.ha | 37++++++++++++++++++++-----------------
Mcmd/hare/schedule.ha | 28+++++++++++++++-------------
Mcmd/hare/subcmds.ha | 281+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mcmd/harec/errors.ha | 6++++--
Mcmd/harec/gen.ha | 141+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mcmd/harec/main.ha | 35+++++++++++++++++++----------------
Mcmd/harec/qbe.ha | 450++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mcmd/harec/qtype.ha | 77+++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mcmd/haredoc/docstr.ha | 211+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mcmd/haredoc/errors.ha | 19+++++++++++++------
Mcmd/haredoc/hare.ha | 122+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mcmd/haredoc/html.ha | 550+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mcmd/haredoc/main.ha | 124+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mcmd/haredoc/resolver.ha | 24++++++++++++++----------
Mcmd/haredoc/sort.ha | 117++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mcmd/haredoc/tty.ha | 355++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mcompress/flate/inflate.ha | 137++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mcompress/zlib/reader.ha | 52+++++++++++++++++++++++++++++++---------------------
Mcrypto/random/+linux.ha | 21+++++++++++++--------
Mcrypto/random/random.ha | 6++++--
Mcrypto/sha512/sha512.ha | 76++++++++++++++++++++++++++++++++++++----------------------------------------
Mdirs/xdg.ha | 27++++++++++++---------------
Mencoding/base64/base64.ha | 119++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mencoding/hex/hex.ha | 6++++--
Mencoding/utf8/decode.ha | 29+++++++++++++++++++----------
Merrors/rt.ha | 29+++++++++++++++++++----------
Merrors/string.ha | 36++++++++++++++++++++++++------------
Mfmt/fmt.ha | 213++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mfnmatch/fnmatch.ha | 237+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mformat/xml/+test.ha | 37++++++++++++++++++-------------------
Mformat/xml/parser.ha | 425++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mformat/xml/types.ha | 13+++++++------
Mfs/fs.ha | 166+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mfs/mem/+test.ha | 12++++++++----
Mfs/mem/mem.ha | 84++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mfs/mem/util.ha | 54+++++++++++++++++++++++++++++-------------------------
Mfs/util.ha | 27++++++++++++++++++---------
Mgetopt/getopts.ha | 58+++++++++++++++++++++++++++++++---------------------------
Mhare/ast/decl.ha | 56++++++++++++++++++++++++++------------------------------
Mhare/ast/expr.ha | 332++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mhare/ast/import.ha | 23+++++++++++------------
Mhare/ast/type.ha | 85+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mhare/lex/+test.ha | 52++++++++++++++++++++++++++++++----------------------
Mhare/lex/lex.ha | 733++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mhare/lex/token.ha | 70+++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mhare/module/context.ha | 30++++++++++++++++--------------
Mhare/module/manifest.ha | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mhare/module/scan.ha | 139+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mhare/module/types.ha | 19++++++++++++-------
Mhare/parse/+test/expr.ha | 43++++++++++++++++++++++++-------------------
Mhare/parse/+test/loc.ha | 46+++++++++++++++++++++++++---------------------
Mhare/parse/+test/roundtrip.ha | 10+++++-----
Mhare/parse/+test/unit.ha | 10+++++-----
Mhare/parse/decl.ha | 119++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mhare/parse/expr.ha | 1035+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mhare/parse/ident.ha | 10++++++----
Mhare/parse/import.ha | 74+++++++++++++++++++++++++++++++++++++-------------------------------------
Mhare/parse/parse.ha | 67+++++++++++++++++++++++++++++++++----------------------------------
Mhare/parse/type.ha | 379+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mhare/types/class.ha | 82++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mhare/types/hash.ha | 174++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mhare/types/lookup.ha | 10+++++-----
Mhare/types/store.ha | 514++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mhare/unit/process.ha | 211+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mhare/unit/scan.ha | 31++++++++++++++++++++-----------
Mhare/unit/scope.ha | 8+++++---
Mhare/unparse/decl.ha | 134+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mhare/unparse/expr.ha | 868+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mhare/unparse/import.ha | 29++++++++++++++---------------
Mhare/unparse/type.ha | 250++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mio/+linux/file.ha | 60+++++++++++++++++++++++++++++++++++++-----------------------
Mio/+test/copy.ha | 50+++++++++++++++++++++++++-------------------------
Mio/+test/limit.ha | 34++++++++++++++++++++++------------
Mio/copy.ha | 28+++++++++++++++++-----------
Mio/drain.ha | 6++++--
Mio/stream.ha | 47+++++++++++++++++++++++++++++------------------
Mio/tee.ha | 9+++++----
Mlinux/io_uring/cqe.ha | 20+++++++++++---------
Mlinux/io_uring/queue.ha | 18++++++++++++------
Mlinux/io_uring/register.ha | 85+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mlinux/io_uring/setup.ha | 24++++++++++++++++--------
Mlinux/signalfd/signalfd.ha | 20+++++++++++---------
Mlinux/vdso/vdso.ha | 56+++++++++++++++++++++++++++++++++++---------------------
Mmath/floats.ha | 64++++++++++++++++++++++++++++++++++++++++------------------------
Mmath/ints.ha | 28++++++++++++++++++----------
Mmath/math.ha | 16++++++++++------
Mnet/+linux.ha | 6++++--
Mnet/dial/ip.ha | 16++++++++++------
Mnet/dial/registry.ha | 14+++++++++-----
Mnet/dial/resolve.ha | 57+++++++++++++++++++++++++++++++--------------------------
Mnet/dns/decode.ha | 23+++++++++++++----------
Mnet/dns/error.ha | 51+++++++++++++++++++++++++++++++++------------------
Mnet/dns/query.ha | 10++++------
Mnet/dns/types.ha | 8+++++---
Mnet/errors.ha | 8+++++---
Mnet/ip/+linux.ha | 35+++++++++++++++++++----------------
Mnet/ip/+test.ha | 6++++--
Mnet/ip/ip.ha | 103+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mnet/listener.ha | 13++++++++-----
Mnet/tcp/+linux.ha | 83+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mnet/udp/+linux.ha | 87+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mnet/unix/+linux.ha | 46+++++++++++++++++++++++++++++-----------------
Mnet/unix/dial.ha | 8+++++---
Mos/+linux/dirfdfs.ha | 136+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mos/+linux/environ.ha | 14+++++++++-----
Mos/+linux/fs.ha | 56+++++++++++++++++++++++++++++++-------------------------
Mos/exec/cmd.ha | 63++++++++++++++++++++++++++++++++++++++++++---------------------
Mos/exec/exec+linux.ha | 79+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mos/exec/process+linux.ha | 45+++++++++++++++++++++++++++++----------------
Mos/exec/types.ha | 8+++++---
Mpath/iter.ha | 7++++---
Mpath/names.ha | 18++++++++++++------
Mrt/+linux/+x86_64.ha | 24+++++++++++++-----------
Mrt/+linux/errno.ha | 792+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mrt/+linux/segmalloc.ha | 20++++++++++----------
Mrt/+linux/syscalls.ha | 117+++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mrt/+x86_64/backtrace.ha | 8+++++---
Mscripts/gen-stdlib | 7-------
Msort/+test.ha | 6++++--
Mstdlib.mk | 86+++++++++++++++++++++++++++----------------------------------------------------
Mstrconv/numeric.ha | 61+++++++++++++++++++++++++++++++++++++++----------------------
Mstrconv/stou.ha | 36+++++++++++++++++++++++++-----------
Mstrings/contains.ha | 6++++--
Mstrings/cstrings.ha | 15+++++++++------
Mstrings/dup.ha | 12++++++++----
Mstrings/index.ha | 20++++++++++++++------
Mstrings/iter.ha | 46+++++++++++++++++++++++++++-------------------
Mstrings/sub.ha | 22++++++++++++++--------
Mstrings/tokenize.ha | 40++++++++++++++++++++++++++--------------
Mstrings/trim.ha | 12++++++++----
Mtemp/+linux.ha | 24++++++++++++++----------
Mtime/+linux/functions.ha | 36++++++++++++++++++++++--------------
Dunicode/README | 30------------------------------
Dunicode/properties.ha | 1130-------------------------------------------------------------------------------
Munix/+linux/nice.ha | 7++++---
Munix/+linux/umask.ha | 8+++++---
Munix/hosts/lookup.ha | 18++++++++++++------
Munix/passwd/group.ha | 33++++++++++++++++++++++-----------
Munix/passwd/passwd.ha | 39++++++++++++++++++++++++++-------------
Munix/poll/+linux.ha | 8+++++---
Munix/resolvconf/load.ha | 18++++++++++++------
Munix/tty/+linux/isatty.ha | 14+++++++++-----
Munix/tty/+linux/open.ha | 9+++++----
Munix/tty/+linux/winsize.ha | 27+++++++++++++++++----------
Muuid/uuid.ha | 38+++++++++++++++++++++++++-------------
153 files changed, 7835 insertions(+), 7026 deletions(-)

diff --git a/ascii/strcmp.ha b/ascii/strcmp.ha @@ -8,15 +8,21 @@ export fn strcmp(a: str, b: str) (int | void) = { let a = strings::iter(a), b = strings::iter(b); for (true) { let ra = match (strings::next(&a)) { - void => return match (strings::next(&b)) { - void => 0, - rune => -1, - }, - r: rune => r, + case void => + match (strings::next(&b)) { + case void => + return 0; + case rune => + return -1; + }; + case r: rune => + yield r; }; let rb = match (strings::next(&b)) { - void => return 1, - r: rune => r, + case void => + return 1; + case r: rune => + yield r; }; if (!isascii(ra) || !isascii(rb)) { return; @@ -33,15 +39,21 @@ export fn strcasecmp(a: str, b: str) (int | void) = { let a = strings::iter(a), b = strings::iter(b); for (true) { let ra = match (strings::next(&a)) { - void => return match (strings::next(&b)) { - void => 0, - rune => -1, - }, - r: rune => r, + case void => + match (strings::next(&b)) { + case void => + return 0; + case rune => + return -1; + }; + case r: rune => + yield r; }; let rb = match (strings::next(&b)) { - void => return 1, - r: rune => r, + case void => + return 1; + case r: rune => + yield r; }; if (!isascii(ra) || !isascii(rb)) { return; diff --git a/bufio/buffered.ha b/bufio/buffered.ha @@ -105,8 +105,10 @@ export fn isbuffered(s: *io::stream) bool = { export fn any_isbuffered(s: *io::stream) bool = { for (!isbuffered(s)) { s = match (io::source(s)) { - errors::unsupported => return false, - s: *io::stream => s, + case errors::unsupported => + return false; + case s: *io::stream => + yield s; }; }; return true; @@ -163,14 +165,15 @@ fn buffered_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { let n = if (len(buf) < len(s.rbuffer)) len(buf) else len(s.rbuffer); if (n > s.ravail) { let z = match (io::read(s.source, s.rbuffer[s.ravail..])) { - err: io::error => return err, - io::EOF => { - if (s.ravail == 0) { - return io::EOF; - }; - yield 0z; - }, - z: size => z, + case err: io::error => + return err; + case io::EOF => + if (s.ravail == 0) { + return io::EOF; + }; + yield 0z; + case z: size => + yield z; }; s.ravail += z; n = if (n > s.ravail) s.ravail else n; diff --git a/bufio/memstream.ha b/bufio/memstream.ha @@ -162,24 +162,21 @@ fn seek( ) (io::off | io::error) = { let s = s: *memstream; switch (w) { - io::whence::SET => { - if (len(s.buf) < off: size) { - abort("invalid offset"); - }; - s.pos = off: size; - }, - io::whence::CUR => { - if (s.pos + off: size > len(s.buf)) { - abort("invalid offset"); - }; - s.pos += off: size; - }, - io::whence::END => { - if (len(s.buf) - (-off): size < len(s.buf)) { - abort("invalid offset"); - }; - s.pos = len(s.buf) - (-off): size; - }, + case io::whence::SET => + if (len(s.buf) < off: size) { + abort("invalid offset"); + }; + s.pos = off: size; + case io::whence::CUR => + if (s.pos + off: size > len(s.buf)) { + abort("invalid offset"); + }; + s.pos += off: size; + case io::whence::END => + if (len(s.buf) - (-off): size < len(s.buf)) { + abort("invalid offset"); + }; + s.pos = len(s.buf) - (-off): size; }; return s.pos: io::off; }; diff --git a/bufio/scanner.ha b/bufio/scanner.ha @@ -8,9 +8,15 @@ use types; export fn scanbyte(stream: *io::stream) (u8 | io::EOF | io::error) = { let buf: [1]u8 = [0...]; - return match (io::read(stream, buf)?) { - read: size => if (read > 0) buf[0] else io::EOF, - io::EOF => io::EOF, + match (io::read(stream, buf)?) { + case read: size => + if (read > 0) { + return buf[0]; + } else { + return io::EOF; + }; + case io::EOF => + return io::EOF; }; }; @@ -21,18 +27,16 @@ export fn scantok(stream: *io::stream, delim: u8...) ([]u8 | io::EOF | io::error for (true) { match (scanbyte(stream)?) { - res: u8 => { - if (bytes::contains(delim, res)) { - break; - }; - append(buf, res); - }, - io::EOF => { - if (len(buf) == 0) { - return io::EOF; - }; + case res: u8 => + if (bytes::contains(delim, res)) { break; - }, + }; + append(buf, res); + case io::EOF => + if (len(buf) == 0) { + return io::EOF; + }; + break; }; }; @@ -48,8 +52,10 @@ export fn scanline(stream: *io::stream) ([]u8 | io::EOF | io::error) = export fn scanrune(stream: *io::stream) (rune | utf8::invalid | io::EOF | io::error) = { let b: [4]u8 = [0...]; match (io::read(stream, b[..1])?) { - n: size => assert(n == 1), - io::EOF => return io::EOF, + case n: size => + assert(n == 1); + case io::EOF => + return io::EOF; }; const sz = utf8::utf8sz(b[0]); @@ -61,16 +67,19 @@ export fn scanrune(stream: *io::stream) (rune | utf8::invalid | io::EOF | io::er return b[0]: u32: rune; }; - match (io::read(stream, b[1..sz])) { - n: size => assert(n == sz - 1), - e: (io::error | io::EOF) => return e, + match (io::read(stream, b[1..sz])?) { + case n: size => + assert(n == sz - 1); + case io::EOF => + return io::EOF; }; let dec = utf8::decode(b[..sz]); - return match (utf8::next(&dec)) { - r: rune => r, - utf8::invalid => utf8::invalid, - (void | utf8::more) => io::EOF, + match (utf8::next(&dec)?) { + case r: rune => + return r; + case (void | utf8::more) => + return io::EOF; }; }; @@ -130,9 +139,12 @@ export fn scanrune(stream: *io::stream) (rune | utf8::invalid | io::EOF | io::er let want = expected[i]; match (scanrune(in)) { - r: rune => assert(want is rune && want as rune == r), - io::EOF => assert(want is io::EOF), - * => abort(), + case r: rune => + assert(want is rune && want as rune == r); + case io::EOF => + assert(want is io::EOF); + case => + abort(); }; }; }; diff --git a/bytes/contains.ha b/bytes/contains.ha @@ -1,5 +1,7 @@ // Returns true if a byte slice contains a byte or a sequence of bytes. export fn contains(haystack: []u8, needle: (u8 | []u8)) bool = match (needle) { - b: u8 => !(index_byte(haystack, b) is void), - b: []u8 => !(index_slice(haystack, b) is void), +case b: u8 => + yield !(index_byte(haystack, b) is void); +case b: []u8 => + yield !(index_slice(haystack, b) is void); }; diff --git a/bytes/index.ha b/bytes/index.ha @@ -2,8 +2,10 @@ // bytes, or void if it is not found. export fn index(haystack: []u8, needle: (u8 | []u8)) (size | void) = { return match (needle) { - b: u8 => index_byte(haystack, b), - b: []u8 => index_slice(haystack, b), + case b: u8 => + yield index_byte(haystack, b); + case b: []u8 => + yield index_slice(haystack, b); }; }; @@ -16,18 +18,23 @@ fn index_byte(haystack: []u8, needle: u8) (size | void) = { }; fn index_slice(haystack: []u8, b: []u8) (size | void) = switch (len(b)) { - 0 => 0, - 1 => index_byte(haystack, b[0]), - // TODO special-case 2, 3, 4 - * => index_tw(haystack, b), +// TODO special-case 2, 3, 4 +case 0 => + yield 0; +case 1 => + yield index_byte(haystack, b[0]); +case => + yield index_tw(haystack, b); }; // Returns the offset of the last instance of "needle" in a "haystack" of // bytes, or void if it is not found. export fn rindex(haystack: []u8, needle: (u8 | []u8)) (size | void) = { - return match (needle) { - b: u8 => rindex_byte(haystack, b), - b: []u8 => rindex_slice(haystack, b), + match (needle) { + case b: u8 => + return rindex_byte(haystack, b); + case b: []u8 => + return rindex_slice(haystack, b); }; }; diff --git a/bytes/tokenize.ha b/bytes/tokenize.ha @@ -21,17 +21,16 @@ export fn tokenize(s: []u8, delim: []u8) tokenizer = { // string starts with, or ends with, a token, an empty slice is returned at the // beginning or end of the sequence, respectively. export fn next_token(s: *tokenizer) ([]u8 | void) = match (peek_token(s)) { - b: []u8 => { - if (s.p == len(s.s)) { - s.d = s.d[..0]; - s.s = s.s[..0]; - } else { - s.s = s.s[s.p + len(s.d)..]; - }; - s.p = types::SIZE_MAX; - return b; - }, - void => void, +case b: []u8 => + if (s.p == len(s.s)) { + s.d = s.d[..0]; + s.s = s.s[..0]; + } else { + s.s = s.s[s.p + len(s.d)..]; + }; + s.p = types::SIZE_MAX; + return b; +case => void; }; // Same as next_token(), but does not advance the cursor @@ -41,8 +40,10 @@ export fn peek_token(s: *tokenizer) ([]u8 | void) = { }; if (s.p > len(s.s)) { s.p = match (index(s.s, s.d)) { - i: size => i, - void => len(s.s), + case i: size => + yield i; + case void => + yield len(s.s); }; }; return s.s[..s.p]; @@ -71,8 +72,10 @@ export fn remaining_tokens(s: *tokenizer) []u8 = { assert(equal(peek_token(&t) as []u8, peek_token(&t) as []u8)); match (next_token(&t)) { - b: []u8 => assert(equal([4, 5], b)), - void => abort(), + case b: []u8 => + assert(equal([4, 5], b)); + case void => + abort(); }; assert(peek_token(&t) is void); @@ -83,20 +86,26 @@ export fn remaining_tokens(s: *tokenizer) []u8 = { assert(equal(peek_token(&t) as []u8, peek_token(&t) as []u8)); match (next_token(&t)) { - b: []u8 => assert(equal([], b)), - void => abort(), + case b: []u8 => + assert(equal([], b)); + case void => + abort(); }; assert(equal(peek_token(&t) as []u8, peek_token(&t) as []u8)); match (next_token(&t)) { - b: []u8 => assert(equal([1], b)), - void => abort(), + case b: []u8 => + assert(equal([1], b)); + case void => + abort(); }; assert(equal(peek_token(&t) as []u8, peek_token(&t) as []u8)); match (next_token(&t)) { - b: []u8 => assert(equal([], b)), - void => abort(), + case b: []u8 => + assert(equal([], b)); + case void => + abort(); }; assert(peek_token(&t) is void); @@ -106,18 +115,24 @@ export fn remaining_tokens(s: *tokenizer) []u8 = { t = tokenize(input3, [1, 2]); match (next_token(&t)) { - b: []u8 => assert(equal([1, 1], b)), - void => abort(), + case b: []u8 => + assert(equal([1, 1], b)); + case void => + abort(); }; match (next_token(&t)) { - b: []u8 => assert(equal([1], b)), - void => abort(), + case b: []u8 => + assert(equal([1], b)); + case void => + abort(); }; match (next_token(&t)) { - b: []u8 => assert(equal([2], b)), - void => abort(), + case b: []u8 => + assert(equal([2], b)); + case void => + abort(); }; assert(next_token(&t) is void); @@ -126,13 +141,17 @@ export fn remaining_tokens(s: *tokenizer) []u8 = { t = tokenize(input4, [1, 2]); match (next_token(&t)) { - b: []u8 => assert(equal([], b)), - void => abort(), + case b: []u8 => + assert(equal([], b)); + case void => + abort(); }; match (next_token(&t)) { - b: []u8 => assert(equal([], b)), - void => abort(), + case b: []u8 => + assert(equal([], b)); + case void => + abort(); }; assert(peek_token(&t) is void); @@ -142,13 +161,17 @@ export fn remaining_tokens(s: *tokenizer) []u8 = { t = tokenize(input5, [24, 42]); match (next_token(&t)) { - b: []u8 => assert(equal([], b)), - void => abort(), + case b: []u8 => + assert(equal([], b)); + case void => + abort(); }; match (next_token(&t)) { - b: []u8 => assert(equal([1], b)), - void => abort(), + case b: []u8 => + assert(equal([1], b)); + case void => + abort(); }; assert(equal(remaining_tokens(&t), [2, 3, 4])); diff --git a/cmd/hare/main.ha b/cmd/hare/main.ha @@ -17,16 +17,21 @@ export fn main() void = { os::exit(1); }; const task = switch (cmd.args[0]) { - "build" => &build, - "cache" => &cache, - "deps" => &deps, - "run" => &run, - "test" => &test, - "version" => &version, - * => { - getopt::printusage(os::stderr, os::args[0], help...); - os::exit(1); - }, + case "build" => + yield &build; + case "cache" => + yield &cache; + case "deps" => + yield &deps; + case "run" => + yield &run; + case "test" => + yield &test; + case "version" => + yield &version; + case => + getopt::printusage(os::stderr, os::args[0], help...); + os::exit(1); }; task(cmd.args); }; diff --git a/cmd/hare/plan.ha b/cmd/hare/plan.ha @@ -47,9 +47,10 @@ type plan = struct { fn mkplan(ctx: *module::context) plan = { const rtdir = match (module::lookup(ctx, ["rt"])) { - err: module::error => fmt::fatal("Error resolving rt: {}", - module::strerror(err)), - ver: module::version => ver.basedir, + case err: module::error => + fmt::fatal("Error resolving rt: {}", module::strerror(err)); + case ver: module::version => + yield ver.basedir; }; return plan { context = ctx, @@ -119,19 +120,21 @@ fn plan_execute(plan: *plan, verbose: bool) (void | !exec::exit_status) = { }; // TODO: This can be a type assertion let task = match (next) { - null => abort(), - t: *task => t, + case null => + abort(); + case t: *task => + yield t; }; match (execute(plan, task, verbose)) { - err: exec::error => fmt::fatal("Error: {}: {}", - task.cmd[0], exec::strerror(err)), - err: !exec::exit_status => { - fmt::errorfln("Error: {}: {}", task.cmd[0], - exec::exitstr(err))!; - return err; - }, - void => void, + case err: exec::error => + fmt::fatal("Error: {}: {}", task.cmd[0], + exec::strerror(err)); + case err: !exec::exit_status => + fmt::errorfln("Error: {}: {}", task.cmd[0], + exec::exitstr(err))!; + return err; + case void => void; }; task.status = status::COMPLETE; @@ -150,10 +153,10 @@ fn update_cache(plan: *plan, mod: modcache) void = { versions = [mod.version], }; match (module::manifest_write(plan.context, &manifest)) { - err: module::error => fmt::fatal( - "Error updating module cache: {}", - module::strerror(err)), - void => void, + case err: module::error => + fmt::fatal("Error updating module cache: {}", + module::strerror(err)); + case void => void; }; }; diff --git a/cmd/hare/schedule.ha b/cmd/hare/schedule.ha @@ -30,12 +30,12 @@ fn sched_module(plan: *plan, ident: ast::ident, link: *[]*task) *task = { }; let ver = match (module::lookup(plan.context, ident)) { - err: module::error => { - let ident = unparse::identstr(ident); - fmt::fatal("Error resolving {}: {}", - ident, module::strerror(err)); - }, - ver: module::version => ver, + case err: module::error => + let ident = unparse::identstr(ident); + fmt::fatal("Error resolving {}: {}", ident, + module::strerror(err)); + case ver: module::version => + yield ver; }; let depends: []*task = []; @@ -188,10 +188,11 @@ fn sched_hare_object( // overwriting with just the latest let manifest = match (module::manifest_load( plan.context, namespace)) { - err: module::error => fmt::fatal( - "Error reading cache entry for {}: {}", - ns, module::strerror(err)), - m: module::manifest => m, + case err: module::error => + fmt::fatal("Error reading cache entry for {}: {}", ns, + module::strerror(err)); + case m: module::manifest => + yield m; }; defer module::manifest_finish(&manifest); current = module::current(&manifest, &ver); @@ -208,9 +209,10 @@ fn sched_hare_object( path = path::join(path, namespace[i]); }; match (os::mkdirs(path)) { - void => void, - err: fs::error => fmt::fatal("Error: mkdirs {}: {}", - path, fs::strerror(err)), + case void => void; + case err: fs::error => + fmt::fatal("Error: mkdirs {}: {}", path, + fs::strerror(err)); }; append(harec.cmd, "-t", path::join(path, td)); yield path::join(path, name); diff --git a/cmd/hare/subcmds.ha b/cmd/hare/subcmds.ha @@ -23,8 +23,10 @@ fn default_tags() []module::tag = { fn addtags(tags: []module::tag, in: str) ([]module::tag | void) = { let in = match (module::parsetags(in)) { - void => return void, - t: []module::tag => t, + case void => + return void; + case t: []module::tag => + yield t; }; defer free(in); append(tags, in...); @@ -37,8 +39,10 @@ fn deltags(tags: []module::tag, in: str) ([]module::tag | void) = { return []; }; let in = match (module::parsetags(in)) { - void => return void, - t: []module::tag => t, + case void => + return void; + case t: []module::tag => + yield t; }; defer free(in); for (let i = 0z; i < len(tags); i += 1) { @@ -85,22 +89,36 @@ fn build(args: []str) void = { for (let i = 0z; i < len(cmd.opts); i += 1) { let opt = cmd.opts[i]; switch (opt.0) { - 'c' => goal = goal::OBJ, - 'v' => verbose = true, - 'D' => append(defines, opt.1), - 'j' => abort(), // TODO - 'l' => abort(), // TODO - 'o' => output = opt.1, - 't' => abort(), // TODO - 'T' => tags = match (addtags(tags, opt.1)) { - void => fmt::fatal("Error parsing tags"), - t: []module::tag => t, - }, - 'X' => tags = match (deltags(tags, opt.1)) { - void => fmt::fatal("Error parsing tags"), - t: []module::tag => t, - }, - * => abort(), + case 'c' => + goal = goal::OBJ; + case 'v' => + verbose = true; + case 'D' => + append(defines, opt.1); + case 'j' => + abort(); // TODO + case 'l' => + abort(); // TODO + case 'o' => + output = opt.1; + case 't' => + abort(); // TODO + case 'T' => + tags = match (addtags(tags, opt.1)) { + case void => + fmt::fatal("Error parsing tags"); + case t: []module::tag => + yield t; + }; + case 'X' => + tags = match (deltags(tags, opt.1)) { + case void => + fmt::fatal("Error parsing tags"); + case t: []module::tag => + yield t; + }; + case => + abort(); }; }; @@ -121,10 +139,11 @@ fn build(args: []str) void = { defer plan_finish(&plan); const ver = match (module::scan(&ctx, input)) { - ver: module::version => ver, - err: module::error => fmt::fatal( - "Error scanning input module: {}", - module::strerror(err)), + case ver: module::version => + yield ver; + case err: module::error => + fmt::fatal("Error scanning input module: {}", + module::strerror(err)); }; const depends: []*task = []; @@ -141,9 +160,9 @@ fn build(args: []str) void = { }; sched_hare_exe(&plan, ver, output, depends...); match (plan_execute(&plan, verbose)) { - void => void, - !exec::exit_status => fmt::fatal("{} {}: build failed", - os::args[0], os::args[1]), + case void => void; + case !exec::exit_status => + fmt::fatal("{} {}: build failed", os::args[0], os::args[1]); }; }; @@ -196,20 +215,32 @@ fn run(args: []str) void = { for (let i = 0z; i < len(cmd.opts); i += 1) { let opt = cmd.opts[i]; switch (opt.0) { - 'v' => verbose = true, - 'D' => append(defines, opt.1), - 'j' => abort(), // TODO - 'l' => abort(), // TODO - 't' => abort(), // TODO - 'T' => tags = match (addtags(tags, opt.1)) { - void => fmt::fatal("Error parsing tags"), - t: []module::tag => t, - }, - 'X' => tags = match (deltags(tags, opt.1)) { - void => fmt::fatal("Error parsing tags"), - t: []module::tag => t, - }, - * => abort(), + case 'v' => + verbose = true; + case 'D' => + append(defines, opt.1); + case 'j' => + abort(); // TODO + case 'l' => + abort(); // TODO + case 't' => + abort(); // TODO + case 'T' => + tags = match (addtags(tags, opt.1)) { + case void => + fmt::fatal("Error parsing tags"); + case t: []module::tag => + yield t; + }; + case 'X' => + tags = match (deltags(tags, opt.1)) { + case void => + fmt::fatal("Error parsing tags"); + case t: []module::tag => + yield t; + }; + case => + abort(); }; }; @@ -229,10 +260,11 @@ fn run(args: []str) void = { defer plan_finish(&plan); const ver = match (module::scan(&ctx, input)) { - ver: module::version => ver, - err: module::error => fmt::fatal( - "Error scanning input module: {}", - module::strerror(err)), + case ver: module::version => + yield ver; + case err: module::error => + fmt::fatal("Error scanning input module: {}", + module::strerror(err)); }; let depends: []*task = []; @@ -246,13 +278,15 @@ fn run(args: []str) void = { const output = mkfile(&plan, "out"); sched_hare_exe(&plan, ver, output, depends...); match (plan_execute(&plan, verbose)) { - void => void, - !exec::exit_status => fmt::fatal("{} {}: build failed", - os::args[0], os::args[1]), + case void => void; + case !exec::exit_status => + fmt::fatal("{} {}: build failed", os::args[0], os::args[1]); }; const cmd = match (exec::cmd(output, runargs...)) { - err: exec::error => fmt::fatal("exec: {}", exec::strerror(err)), - cmd: exec::command => cmd, + case err: exec::error => + fmt::fatal("exec: {}", exec::strerror(err)); + case cmd: exec::command => + yield cmd; }; exec::setname(&cmd, input); exec::exec(&cmd); @@ -264,40 +298,47 @@ fn sched_walk(plan: *plan, ident: ast::ident, link: *[]*task) void = { free(path); for (true) :loop { match (fs::next(it)) { - ent: fs::dirent => { - if (ent.name == "." || ent.name == "..") { - continue; - }; - if (ent.ftype & fs::mode::DIR != fs::mode::DIR) { - continue; - }; - const d = utf8::decode(ent.name); - match (utf8::next(&d)) { - void => break, - (utf8::more | utf8::invalid) => continue :loop, - r: rune => if (!ascii::isalpha(r) && r != '_') { - continue :loop; - }, - }; - for (true) match (utf8::next(&d)) { - void => break, - (utf8::more | utf8::invalid) => continue :loop, - r: rune => if (!ascii::isalnum(r) && r != '_') { - continue :loop; - }, + case ent: fs::dirent => + if (ent.name == "." || ent.name == "..") { + continue; + }; + if (ent.ftype & fs::mode::DIR != fs::mode::DIR) { + continue; + }; + const d = utf8::decode(ent.name); + match (utf8::next(&d)) { + case void => + break; + case (utf8::more | utf8::invalid) => + continue :loop; + case r: rune => + if (!ascii::isalpha(r) && r != '_') { + continue :loop; }; - let new = ast::ident_dup(ident); - append(new, strings::dup(ent.name)); - sched_walk(plan, new, link); - - match (module::lookup(plan.context, new)) { - ver: module::version => - if (len(ver.inputs) == 0) continue, - module::error => continue, + }; + for (true) match (utf8::next(&d)) { + case void => + break; + case (utf8::more | utf8::invalid) => + continue :loop; + case r: rune => + if (!ascii::isalnum(r) && r != '_') { + continue :loop; }; - sched_module(plan, new, link); - }, - void => break, + }; + let new = ast::ident_dup(ident); + append(new, strings::dup(ent.name)); + sched_walk(plan, new, link); + + match (module::lookup(plan.context, new)) { + case ver: module::version => + if (len(ver.inputs) == 0) continue; + case module::error => + continue; + }; + sched_module(plan, new, link); + case void => + break; }; }; }; @@ -330,21 +371,34 @@ fn test(args: []str) void = { for (let i = 0z; i < len(cmd.opts); i += 1) { const opt = cmd.opts[i]; switch (opt.0) { - 'v' => verbose = true, - 'D' => append(defines, opt.1), - 'j' => abort(), // TODO - 'l' => abort(), // TODO - 't' => abort(), // TODO - 'o' => output = opt.1, - 'T' => tags = match (addtags(tags, opt.1)) { - void => fmt::fatal("Error parsing tags"), - t: []module::tag => t, - }, - 'X' => tags = match (deltags(tags, opt.1)) { - void => fmt::fatal("Error parsing tags"), - t: []module::tag => t, - }, - * => abort(), + case 'v' => + verbose = true; + case 'D' => + append(defines, opt.1); + case 'j' => + abort(); // TODO + case 'l' => + abort(); // TODO + case 't' => + abort(); // TODO + case 'o' => + output = opt.1; + case 'T' => + tags = match (addtags(tags, opt.1)) { + case void => + fmt::fatal("Error parsing tags"); + case t: []module::tag => + yield t; + }; + case 'X' => + tags = match (deltags(tags, opt.1)) { + case void => + fmt::fatal("Error parsing tags"); + case t: []module::tag => + yield t; + }; + case => + abort(); }; }; @@ -364,10 +418,11 @@ fn test(args: []str) void = { defer plan_finish(&plan); const ver = match (module::scan(&ctx, input)) { - ver: module::version => ver, - err: module::error => fmt::fatal( - "Error scanning input module: {}", - module::strerror(err)), + case ver: module::version => + yield ver; + case err: module::error => + fmt::fatal("Error scanning input module: {}", + module::strerror(err)); }; let depends: []*task = []; @@ -384,9 +439,9 @@ fn test(args: []str) void = { sched_hare_exe(&plan, ver, strings::dup(output), depends...); }; match (plan_execute(&plan, verbose)) { - void => void, - !exec::exit_status => fmt::fatal("{} {}: build failed", - os::args[0], os::args[1]), + case void => void; + case !exec::exit_status => + fmt::fatal("{} {}: build failed", os::args[0], os::args[1]); }; if (have_output) { @@ -394,8 +449,10 @@ fn test(args: []str) void = { }; const cmd = match (exec::cmd(output, runargs...)) { - err: exec::error => fmt::fatal("exec: {}", exec::strerror(err)), - cmd: exec::command => cmd, + case err: exec::error => + fmt::fatal("exec: {}", exec::strerror(err)); + case cmd: exec::command => + yield cmd; }; exec::setname(&cmd, input); exec::exec(&cmd); @@ -429,12 +486,12 @@ fn version(args: []str) void = { fmt::println()!; match (os::getenv("HAREPATH")) { - void => fmt::printfln("HAREPATH\t{}", HAREPATH)!, - s: str => { - fmt::printf("HAREPATH\t{}", s)!; - bufio::flush(os::stdout)!; - fmt::errorln("\t(from environment)")!; - }, + case void => + fmt::printfln("HAREPATH\t{}", HAREPATH)!; + case s: str => + fmt::printf("HAREPATH\t{}", s)!; + bufio::flush(os::stdout)!; + fmt::errorln("\t(from environment)")!; }; }; }; diff --git a/cmd/harec/errors.ha b/cmd/harec/errors.ha @@ -9,8 +9,10 @@ use strings; // TODO: Expand to more kinds of errors fn printerr(err: parse::error) void = { match (err) { - err: lex::syntax => printerr_syntax(err), - err: io::error => fmt::errorln(io::strerror(err))!, + case err: lex::syntax => + printerr_syntax(err); + case err: io::error => + fmt::errorln(io::strerror(err))!; }; }; diff --git a/cmd/harec/gen.ha b/cmd/harec/gen.ha @@ -25,9 +25,12 @@ fn gen(store: *types::typestore, unit: *unit::unit) void = { ... }; defer io::close(ctx.out); - for (let i = 0z; i < len(unit.decls); i += 1) match (unit.decls[i].decl) { - func: unit::decl_func => gen_func(&ctx, &unit.decls[i]), - * => abort(), // TODO + for (let i = 0z; i < len(unit.decls); i += 1) { + match (unit.decls[i].decl) { + case func: unit::decl_func => + gen_func(&ctx, &unit.decls[i]); + case => abort(); // TODO + }; }; }; @@ -35,8 +38,10 @@ fn gen_func(ctx: *context, decl: *unit::decl) void = { // TODO: const fndecl = &decl.decl as *unit::decl_func; const fndecl = decl.decl as unit::decl_func; const body = match (fndecl.body) { - null => return, // Prototype - expr: *unit::expr => expr, + case expr: *unit::expr => + yield expr; + case null => + return; // Prototype }; const fntype = fndecl.prototype.repr as types::func; assert(fntype.flags == 0); @@ -47,10 +52,13 @@ fn gen_func(ctx: *context, decl: *unit::decl) void = { fmt::printf("{}function section \".text.{}\" \"ax\"", if (decl.exported) "export " else "", ident)!; const rtype = fntype.result; - const has_rval = match (types::dealias(rtype).repr) { - bi: types::builtin => bi != types::builtin::VOID, - * => true, - }; + const has_rval = + match (types::dealias(rtype).repr) { + case bi: types::builtin => + yield bi != types::builtin::VOID; + case => + yield true; + }; if (has_rval) { const qrtype = qtype_lookup(ctx, rtype, false); fmt::printf(" {}", qtype_repr(qrtype))!; @@ -83,14 +91,15 @@ fn gen_func(ctx: *context, decl: *unit::decl) void = { fn gen_store(ctx: *context, object: value, value: value) void = { match (types::dealias(object._type).repr) { - bi: types::builtin => switch (bi) { - builtin::STR => abort(), // TODO - * => void, - }, - types::_struct => abort(), // TODO - (types::array | types::slice | types::tagged | types::tuple) => - abort(), // TODO - * => void, + case bi: types::builtin => + switch (bi) { + case builtin::STR => abort(); // TODO + case => void; + }; + case types::_struct => abort(); // TODO + case (types::array | types::slice | types::tagged | types::tuple) => + abort(); // TODO + case => void; // Handled below }; const instr = qinstr_store(ctx, object._type); emit(ctx.out, void, instr, value, object); @@ -98,14 +107,15 @@ fn gen_store(ctx: *context, object: value, value: value) void = { fn gen_load(ctx: *context, object: value) value = { match (types::dealias(object._type).repr) { - bi: types::builtin => switch (bi) { - builtin::STR => abort(), // TODO - * => void, - }, - types::_struct => abort(), // TODO - (types::array | types::slice | types::tagged | types::tuple) => - abort(), // TODO - * => void, + case bi: types::builtin => + switch (bi) { + case builtin::STR => abort(); // TODO + case => void; + }; + case types::_struct => abort(); // TODO + case (types::array | types::slice | types::tagged | types::tuple) => + abort(); // TODO + case => void; // Handled below }; const instr = qinstr_load(ctx, object._type); const temp = mktemp(ctx); @@ -118,45 +128,48 @@ fn gen_load(ctx: *context, object: value) value = { }; fn gen_expr(ctx: *context, expr: *unit::expr) value = { - return match (expr.expr) { - unit::access => gen_access(ctx, expr), - unit::bindings => gen_binding(ctx, expr), - unit::compound => gen_compound(ctx, expr), - unit::constant => gen_const(ctx, expr), - unit::_return => gen_return(ctx, expr), + match (expr.expr) { + case unit::access => + return gen_access(ctx, expr); + case unit::bindings => + return gen_binding(ctx, expr); + case unit::compound => + return gen_compound(ctx, expr); + case unit::constant => + return gen_const(ctx, expr); + case unit::_return => + return gen_return(ctx, expr); }; }; fn gen_expr_at(ctx: *context, expr: *unit::expr, at: value) void = { match (expr.expr) { - // TODO: Some functions will prefer _at - * => void, + case => void; // TODO: Some functions will prefer _at }; const value = gen_expr(ctx, expr); gen_store(ctx, at, value); }; fn gen_access_object(ctx: *context, object: *unit::object) value = { - return switch (object.kind) { - object_kind::CONST, object_kind::TYPE => abort(), - object_kind::BIND => { - const binding = binding_lookup(ctx, object); - yield value { - value = binding.name, - _type = object._type, - }; - }, - object_kind::DECL => abort(), // TODO + switch (object.kind) { + case object_kind::CONST, object_kind::TYPE => abort(); + case object_kind::BIND => + const binding = binding_lookup(ctx, object); + return value { + value = binding.name, + _type = object._type, + }; + case object_kind::DECL => abort(); // TODO }; }; fn gen_access_addr(ctx: *context, expr: *unit::expr) value = { - const access = expr.expr as unit::access; - return match (access) { - ao: unit::access_object => gen_access_object(ctx, ao), - ai: unit::access_index => abort(), // TODO - af: unit::access_field => abort(), // TODO - at: unit::access_tuple => abort(), // TODO + match (expr.expr as unit::access) { + case ao: unit::access_object => + return gen_access_object(ctx, ao); + case ai: unit::access_index => abort(); // TODO + case af: unit::access_field => abort(); // TODO + case at: unit::access_tuple => abort(); // TODO }; }; @@ -199,14 +212,20 @@ fn gen_compound(ctx: *context, expr: *unit::expr) value = { fn gen_const(ctx: *context, expr: *unit::expr) value = { const constexpr = expr.expr as unit::constant; const val: qval = match (constexpr) { - void => return vvoid, - b: bool => (if (b) 1u32 else 0u32): constant, - ast::_null => 0u64, // XXX: Arch - r: rune => r: u32, - v: (u64 | f64) => v, - i: i64 => i: u64, - s: str => abort(), // TODO - // TODO: Aggregate types + case void => + return vvoid; + case b: bool => + yield (if (b) 1u32 else 0u32): constant; + case ast::_null => + yield 0u64; // XXX: Arch + case r: rune => + yield r: u32; + case v: (u64 | f64) => + yield v; + case i: i64 => + yield i: u64; + case s: str => abort(); // TODO + // TODO: Aggregate types }; return value { value = val, @@ -217,10 +236,10 @@ fn gen_const(ctx: *context, expr: *unit::expr) value = { fn gen_return(ctx: *context, expr: *unit::expr) value = { const rexpr = expr.expr as unit::_return; match (rexpr) { - null => emit(ctx.out, void, qinstr::RET), - expr: *unit::expr => { - emit(ctx.out, void, qinstr::RET, gen_expr(ctx, expr)); - }, + case null => + emit(ctx.out, void, qinstr::RET); + case expr: *unit::expr => + emit(ctx.out, void, qinstr::RET, gen_expr(ctx, expr)); }; return vvoid; }; diff --git a/cmd/harec/main.ha b/cmd/harec/main.ha @@ -26,12 +26,12 @@ export fn main() void = { for (let i = 0z; i < len(cmd.opts); i += 1) { let opt = cmd.opts[i]; switch (opt.0) { - 'D' => abort(), // TODO - 'N' => abort(), // TODO - 'T' => abort(), // TODO - 'o' => abort(), // TODO - 't' => abort(), // TODO - * => abort(), + case 'D' => abort(); // TODO + case 'N' => abort(); // TODO + case 'T' => abort(); // TODO + case 'o' => abort(); // TODO + case 't' => abort(); // TODO + case => abort(); }; }; @@ -51,9 +51,11 @@ export fn main() void = { for (let i = 0z; i < len(cmd.args); i += 1) { let input = match (os::open(cmd.args[i])) { - f: io::file => f, - err: io::error => fmt::fatal("Error opening {}: {}", - cmd.args[i], io::strerror(err)), + case f: io::file => + yield f; + case err: io::error => + fmt::fatal("Error opening {}: {}", + cmd.args[i], io::strerror(err)); }; defer io::close(&input); static let buf: [os::BUFSIZ]u8 = [0...]; @@ -62,18 +64,19 @@ export fn main() void = { let lexer = lex::init(bufin, cmd.args[i]); let su = match (parse::subunit(&lexer)) { - err: parse::error => { - printerr(err); - os::exit(1); - }, - u: ast::subunit => u, + case err: parse::error => + printerr(err); + os::exit(1); + case u: ast::subunit => + yield u; }; append(subunits, su); }; let unit = match (unit::check(store, [], subunits)) { - unit::error => abort(), // TODO - u: unit::unit => u, + case unit::error => abort(); // TODO + case u: unit::unit => + yield u; }; defer unit::unit_free(unit); gen(store, &unit); diff --git a/cmd/harec/qbe.ha b/cmd/harec/qbe.ha @@ -29,111 +29,199 @@ fn emit( ) void = { fmt::fprintf(to, "\t")!; match (out) { - val: qtypeval => { - const ty = val.0, val = val.1; - qval_emit(to, val); - fmt::fprintf(to, " ={} ", qtype_repr(ty))!; - }, - void => void, + case val: qtypeval => + const ty = val.0, val = val.1; + qval_emit(to, val); + fmt::fprintf(to, " ={} ", qtype_repr(ty))!; + case void => void; }; // TODO: The langauge should provide a means of converting enums to // strings without this fmt::fprint(to, switch (instr) { - qinstr::ADD => "add", - qinstr::ALLOC16 => "alloc16", - qinstr::ALLOC4 => "alloc4", - qinstr::ALLOC8 => "alloc8", - qinstr::AND => "and", - qinstr::CALL => "call", - qinstr::CAST => "cast", - qinstr::CEQD => "ceqd", - qinstr::CEQL => "ceql", - qinstr::CEQS => "ceqs", - qinstr::CEQW => "ceqw", - qinstr::CGED => "cged", - qinstr::CGES => "cges", - qinstr::CGTD => "cgtd", - qinstr::CGTS => "cgts", - qinstr::CLED => "cled", - qinstr::CLES => "cles", - qinstr::CLTD => "cltd", - qinstr::CLTS => "clts", - qinstr::CNED => "cned", - qinstr::CNEL => "cnel", - qinstr::CNES => "cnes", - qinstr::CNEW => "cnew", - qinstr::COD => "cod", - qinstr::COPY => "copy", - qinstr::COS => "cos", - qinstr::CSGEL => "csgel", - qinstr::CSGEW => "csgew", - qinstr::CSGTL => "csgtl", - qinstr::CSGTW => "csgtw", - qinstr::CSLEL => "cslel", - qinstr::CSLEW => "cslew", - qinstr::CSLTL => "csltl", - qinstr::CSLTW => "csltw", - qinstr::CUGEL => "cugel", - qinstr::CUGEW => "cugew", - qinstr::CUGTL => "cugtl", - qinstr::CUGTW => "cugtw", - qinstr::CULEL => "culel", - qinstr::CULEW => "culew", - qinstr::CULTL => "cultl", - qinstr::CULTW => "cultw", - qinstr::CUOD => "cuod", - qinstr::CUOS => "cuos", - qinstr::DIV => "div", - qinstr::DTOSI => "dtosi", - qinstr::EXTS => "exts", - qinstr::EXTSB => "extsb", - qinstr::EXTSH => "extsh", - qinstr::EXTSW => "extsw", - qinstr::EXTUB => "extub", - qinstr::EXTUH => "extuh", - qinstr::EXTUW => "extuw", - qinstr::JMP => "jmp", - qinstr::JNZ => "jnz", - qinstr::LOADD => "loadd", - qinstr::LOADL => "loadl", - qinstr::LOADS => "loads", - qinstr::LOADSB => "loadsb", - qinstr::LOADSH => "loadsh", - qinstr::LOADSW => "loadsw", - qinstr::LOADUB => "loadub", - qinstr::LOADUH => "loaduh", - qinstr::LOADUW => "loaduw", - qinstr::MUL => "mul", - qinstr::OR => "or", - qinstr::REM => "rem", - qinstr::RET => "ret", - qinstr::SAR => "sar", - qinstr::SHL => "shl", - qinstr::SHR => "shr", - qinstr::SLTOF => "sltof", - qinstr::STOREB => "storeb", - qinstr::STORED => "stored", - qinstr::STOREH => "storeh", - qinstr::STOREL => "storel", - qinstr::STORES => "stores", - qinstr::STOREW => "storew", - qinstr::STOSI => "stosi", - qinstr::SUB => "sub", - qinstr::SWTOF => "swtof", - qinstr::TRUNCD => "truncd", - qinstr::UDIV => "udiv", - qinstr::UREM => "urem", - qinstr::XOR => "xor", + case qinstr::ADD => + yield "add"; + case qinstr::ALLOC16 => + yield "alloc16"; + case qinstr::ALLOC4 => + yield "alloc4"; + case qinstr::ALLOC8 => + yield "alloc8"; + case qinstr::AND => + yield "and"; + case qinstr::CALL => + yield "call"; + case qinstr::CAST => + yield "cast"; + case qinstr::CEQD => + yield "ceqd"; + case qinstr::CEQL => + yield "ceql"; + case qinstr::CEQS => + yield "ceqs"; + case qinstr::CEQW => + yield "ceqw"; + case qinstr::CGED => + yield "cged"; + case qinstr::CGES => + yield "cges"; + case qinstr::CGTD => + yield "cgtd"; + case qinstr::CGTS => + yield "cgts"; + case qinstr::CLED => + yield "cled"; + case qinstr::CLES => + yield "cles"; + case qinstr::CLTD => + yield "cltd"; + case qinstr::CLTS => + yield "clts"; + case qinstr::CNED => + yield "cned"; + case qinstr::CNEL => + yield "cnel"; + case qinstr::CNES => + yield "cnes"; + case qinstr::CNEW => + yield "cnew"; + case qinstr::COD => + yield "cod"; + case qinstr::COPY => + yield "copy"; + case qinstr::COS => + yield "cos"; + case qinstr::CSGEL => + yield "csgel"; + case qinstr::CSGEW => + yield "csgew"; + case qinstr::CSGTL => + yield "csgtl"; + case qinstr::CSGTW => + yield "csgtw"; + case qinstr::CSLEL => + yield "cslel"; + case qinstr::CSLEW => + yield "cslew"; + case qinstr::CSLTL => + yield "csltl"; + case qinstr::CSLTW => + yield "csltw"; + case qinstr::CUGEL => + yield "cugel"; + case qinstr::CUGEW => + yield "cugew"; + case qinstr::CUGTL => + yield "cugtl"; + case qinstr::CUGTW => + yield "cugtw"; + case qinstr::CULEL => + yield "culel"; + case qinstr::CULEW => + yield "culew"; + case qinstr::CULTL => + yield "cultl"; + case qinstr::CULTW => + yield "cultw"; + case qinstr::CUOD => + yield "cuod"; + case qinstr::CUOS => + yield "cuos"; + case qinstr::DIV => + yield "div"; + case qinstr::DTOSI => + yield "dtosi"; + case qinstr::EXTS => + yield "exts"; + case qinstr::EXTSB => + yield "extsb"; + case qinstr::EXTSH => + yield "extsh"; + case qinstr::EXTSW => + yield "extsw"; + case qinstr::EXTUB => + yield "extub"; + case qinstr::EXTUH => + yield "extuh"; + case qinstr::EXTUW => + yield "extuw"; + case qinstr::JMP => + yield "jmp"; + case qinstr::JNZ => + yield "jnz"; + case qinstr::LOADD => + yield "loadd"; + case qinstr::LOADL => + yield "loadl"; + case qinstr::LOADS => + yield "loads"; + case qinstr::LOADSB => + yield "loadsb"; + case qinstr::LOADSH => + yield "loadsh"; + case qinstr::LOADSW => + yield "loadsw"; + case qinstr::LOADUB => + yield "loadub"; + case qinstr::LOADUH => + yield "loaduh"; + case qinstr::LOADUW => + yield "loaduw"; + case qinstr::MUL => + yield "mul"; + case qinstr::OR => + yield "or"; + case qinstr::REM => + yield "rem"; + case qinstr::RET => + yield "ret"; + case qinstr::SAR => + yield "sar"; + case qinstr::SHL => + yield "shl"; + case qinstr::SHR => + yield "shr"; + case qinstr::SLTOF => + yield "sltof"; + case qinstr::STOREB => + yield "storeb"; + case qinstr::STORED => + yield "stored"; + case qinstr::STOREH => + yield "storeh"; + case qinstr::STOREL => + yield "storel"; + case qinstr::STORES => + yield "stores"; + case qinstr::STOREW => + yield "storew"; + case qinstr::STOSI => + yield "stosi"; + case qinstr::SUB => + yield "sub"; + case qinstr::SWTOF => + yield "swtof"; + case qinstr::TRUNCD => + yield "truncd"; + case qinstr::UDIV => + yield "udiv"; + case qinstr::UREM => + yield "urem"; + case qinstr::XOR => + yield "xor"; })!; for (let i = 0z; i < len(args); i += 1) { const arg = match (args[i]) { - v: value => v.value, - * => args[i], + case v: value => + yield v.value; + case => + yield args[i]; }; match (arg) { - v: value => abort(), // Invariant - qv: qval => qval_emit(to, qv), - qt: qtype => abort(), // TODO + case v: value => abort(); // Invariant + case qv: qval => + yield qval_emit(to, qv); + case qt: qtype => + abort(); // TODO }; if (i + 1 < len(args)) { fmt::fprint(to, ",")!; @@ -142,85 +230,121 @@ fn emit( fmt::fprintln(to)!; }; -fn qval_emit(to: *io::stream, qv: qval) void = match (qv) { - g: global => fmt::fprintf(to, " ${}", g)!, - t: temporary => fmt::fprintf(to, " %{}", t)!, - l: label => fmt::fprintf(to, " @{}", l)!, - c: constant => match (c) { - qvoid => abort(), - v: (u32 | u64 | f32 | f64 | str) => fmt::fprintf(to, " {}", v)!, - }, +fn qval_emit(to: *io::stream, qv: qval) void = { + match (qv) { + case g: global => + fmt::fprintf(to, " ${}", g)!; + case t: temporary => + fmt::fprintf(to, " %{}", t)!; + case l: label => + fmt::fprintf(to, " @{}", l)!; + case c: constant => + match (c) { + case qvoid => abort(); + case v: (u32 | u64 | f32 | f64 | str) => + fmt::fprintf(to, " {}", v)!; + }; + }; }; -fn qinstr_alloc(ty: *types::_type) qinstr = switch (ty.align) { - 4 => qinstr::ALLOC4, - 8 => qinstr::ALLOC8, - 16 => qinstr::ALLOC16, - * => abort(), +fn qinstr_alloc(ty: *types::_type) qinstr = { + switch (ty.align) { + case 4 => + return qinstr::ALLOC4; + case 8 => + return qinstr::ALLOC8; + case 16 => + return qinstr::ALLOC16; + case => abort(); + }; }; fn qinstr_store( ctx: *context, ty: *types::_type, -) qinstr = match (ty.repr) { - al: types::alias => qinstr_store(ctx, al.secondary: *types::_type), - bi: types::builtin => switch (bi) { - builtin::STR, builtin::VOID => abort(), - builtin::F32 => qinstr::STORES, - builtin::F64 => qinstr::STORED, - * => switch (ty.sz) { - 1 => qinstr::STOREB, - 2 => qinstr::STOREH, - 4 => qinstr::STOREW, - 8 => qinstr::STOREL, - * => abort(), - }, - }, - types::pointer => if (ctx.arch.ptr == &qlong) { - yield qinstr::STOREL; - } else if (ctx.arch.ptr == &qword) { - yield qinstr::STOREW; - } else abort(), - en: types::_enum => abort(), // TODO - * => abort(), +) qinstr = { + match (ty.repr) { + case al: types::alias => + return qinstr_store(ctx, al.secondary: *types::_type); + case bi: types::builtin => + switch (bi) { + case builtin::STR, builtin::VOID => abort(); + case builtin::F32 => + return qinstr::STORES; + case builtin::F64 => + return qinstr::STORED; + case => + switch (ty.sz) { + case 1 => + return qinstr::STOREB; + case 2 => + return qinstr::STOREH; + case 4 => + return qinstr::STOREW; + case 8 => + return qinstr::STOREL; + case => abort(); + }; + }; + case types::pointer => + if (ctx.arch.ptr == &qlong) { + return qinstr::STOREL; + } else if (ctx.arch.ptr == &qword) { + return qinstr::STOREW; + } else abort(); + case en: types::_enum => abort(); // TODO + case => abort(); + }; }; fn qinstr_load( ctx: *context, ty: *types::_type, -) qinstr = match (ty.repr) { - al: types::alias => qinstr_load(ctx, al.secondary: *types::_type), - bi: types::builtin => switch (bi) { - builtin::STR, builtin::VOID => abort(), - builtin::F32 => qinstr::LOADS, - builtin::F64 => qinstr::LOADD, - * => switch (ty.sz) { - 1 => if (types::is_signed(ty)) { - yield qinstr::LOADSB; - } else { - yield qinstr::LOADUB; - }, - 2 => if (types::is_signed(ty)) { - yield qinstr::LOADSH; - } else { - yield qinstr::LOADUH; - }, - 4 => if (types::is_signed(ty)) { - yield qinstr::LOADSW; - } else { - yield qinstr::LOADUW; - }, - 8 => qinstr::LOADL, - * => abort(), - }, - }, - types::pointer => if (ctx.arch.ptr == &qlong) { - yield qinstr::LOADL; - } else if (ctx.arch.ptr == &qword) { - yield qinstr::LOADUW; - } else abort(), - en: types::_enum => abort(), // TODO - * => abort(), +) qinstr = { + match (ty.repr) { + case al: types::alias => + return qinstr_load(ctx, al.secondary: *types::_type); + case bi: types::builtin => + switch (bi) { + case builtin::STR, builtin::VOID => abort(); + case builtin::F32 => + return qinstr::LOADS; + case builtin::F64 => + return qinstr::LOADD; + case => + switch (ty.sz) { + case 1 => + if (types::is_signed(ty)) { + return qinstr::LOADSB; + } else { + return qinstr::LOADUB; + }; + case 2 => + if (types::is_signed(ty)) { + return qinstr::LOADSH; + } else { + return qinstr::LOADUH; + }; + case 4 => + if (types::is_signed(ty)) { + return qinstr::LOADSW; + } else { + return qinstr::LOADUW; + }; + case 8 => + return qinstr::LOADL; + case => abort(); + }; + }; + case types::pointer => + if (ctx.arch.ptr == &qlong) { + return qinstr::LOADL; + } else if (ctx.arch.ptr == &qword) { + return qinstr::LOADUW; + } else abort(); + case en: types::_enum => abort(); // TODO + case => abort(); + }; }; type qinstr = enum { diff --git a/cmd/harec/qtype.ha b/cmd/harec/qtype.ha @@ -36,43 +36,56 @@ fn qtype_lookup( ctx: *context, _type: const *types::_type, extype: bool, -) const *qtype = match (_type.repr) { - bi: builtin => switch (bi) { - builtin::CHAR => - if (extype) &qbyte else &qword, - builtin::I8, builtin::U8 => - if (extype) &qbyte else &qword, - builtin::I16, builtin::U16 => - if (extype) &qhalf else &qword, - builtin::BOOL => &qword, - builtin::F32 => &qsingle, - builtin::F64 => &qdouble, - builtin::I32, builtin::U32, builtin::RUNE => &qword, - builtin::I64, builtin::U64 => &qlong, - builtin::INT, builtin::UINT, - builtin::NULL, builtin::UINTPTR, - builtin::SIZE => switch (_type.sz) { - 4 => &qword, - 8 => &qlong, - * => abort(), - }, - builtin::STR => qtype_aggregate(ctx, _type), - builtin::VOID => abort(), - }, - * => abort(), +) const *qtype = { + match (_type.repr) { + case bi: builtin => + switch (bi) { + case builtin::CHAR => + return if (extype) &qbyte else &qword; + case builtin::I8, builtin::U8 => + return if (extype) &qbyte else &qword; + case builtin::I16, builtin::U16 => + return if (extype) &qhalf else &qword; + case builtin::BOOL => + return &qword; + case builtin::F32 => + return &qsingle; + case builtin::F64 => + return &qdouble; + case builtin::I32, builtin::U32, builtin::RUNE => + return &qword; + case builtin::I64, builtin::U64 => + return &qlong; + case builtin::INT, builtin::UINT, builtin::NULL, + builtin::UINTPTR, builtin::SIZE => + switch (_type.sz) { + case 4 => + return &qword; + case 8 => + return &qlong; + case => abort(); + }; + case builtin::STR => + return qtype_aggregate(ctx, _type); + case builtin::VOID => abort(); + }; + case => abort(); + }; }; fn qtype_aggregate(ctx: *context, _type: const *types::_type) const *qtype = { abort(); // TODO }; -// XXX: This dereference should not be necessary after the match -// overhaul -fn qtype_repr(qtype: const *qtype) const str = match (*qtype) { - st: stype => { +fn qtype_repr(qtype: const *qtype) const str = { + // XXX: This dereference should not be necessary after the match + // overhaul + match (*qtype) { + case st: stype => static let buf: [1]u8 = [0...]; - yield fmt::bsprintf(buf, "{}", st: rune); - }, - qc: qcompound => abort(), // TODO - void => "", + return fmt::bsprintf(buf, "{}", st: rune); + case qc: qcompound => abort(); // TODO + case void => + return ""; + }; }; diff --git a/cmd/haredoc/docstr.ha b/cmd/haredoc/docstr.ha @@ -34,24 +34,33 @@ fn parsedoc(in: *io::stream) parser = { }; fn scandoc(par: *parser) (token | void) = { - const rn = match (bufio::scanrune(par.src)) { - io::EOF => return, - rn: rune => rn, - * => abort(), + const rn = match (bufio::scanrune(par.src)!) { + case rn: rune => + yield rn; + case io::EOF => + return; }; bufio::unreadrune(par.src, rn); - return switch (par.state) { - docstate::TEXT => switch (rn) { - '[' => scanref(par), - * => scantext(par), - }, - docstate::PARAGRAPH => switch (rn) { - ' ', '\t' => scansample(par), - '[' => scanref(par), - '-' => scanlist(par), - * => scantext(par), - }, + switch (par.state) { + case docstate::TEXT => + switch (rn) { + case '[' => + return scanref(par); + case => + return scantext(par); + }; + case docstate::PARAGRAPH => + switch (rn) { + case ' ', '\t' => + return scansample(par); + case '[' => + return scanref(par); + case '-' => + return scanlist(par); + case => + return scantext(par); + }; }; }; @@ -63,31 +72,29 @@ fn scantext(par: *parser) (token | void) = { // TODO: Collapse whitespace const buf = strio::dynamic(); for (true) { - const rn = match (bufio::scanrune(par.src)) { - io::EOF => break, - utf8::invalid => abort("Invalid UTF-8"), - err: io::error => fmt::fatal(io::strerror(err)), - rn: rune => rn, + const rn = match (bufio::scanrune(par.src)!) { + case io::EOF => break; + case rn: rune => + yield rn; }; switch (rn) { - '[' => { - bufio::unreadrune(par.src, rn); + case '[' => + bufio::unreadrune(par.src, rn); + break; + case '\n' => + strio::appendrune(buf, rn)!; + const rn = match (bufio::scanrune(par.src)!) { + case io::EOF => break; + case rn: rune => + yield rn; + }; + if (rn == '\n') { + par.state = docstate::PARAGRAPH; break; - }, - '\n' => { - strio::appendrune(buf, rn)!; - const rn = match (bufio::scanrune(par.src)) { - io::EOF => break, - * => abort(), - rn: rune => rn, - }; - if (rn == '\n') { - par.state = docstate::PARAGRAPH; - break; - }; - bufio::unreadrune(par.src, rn); - }, - * => strio::appendrune(buf, rn)!, + }; + bufio::unreadrune(par.src, rn); + case => + strio::appendrune(buf, rn)!; }; }; let result = strio::finish(buf); @@ -98,33 +105,39 @@ fn scantext(par: *parser) (token | void) = { }; fn scanref(par: *parser) (token | void) = { - match (bufio::scanrune(par.src)) { - io::EOF => return, - rn: rune => if (rn != '[') abort(), - * => abort(), + match (bufio::scanrune(par.src)!) { + case io::EOF => + return; + case rn: rune => + if (rn != '[') { + abort(); + }; }; - match (bufio::scanrune(par.src)) { - io::EOF => return, - rn: rune => if (rn != '[') { + match (bufio::scanrune(par.src)!) { + case io::EOF => + return; + case rn: rune => + if (rn != '[') { bufio::unreadrune(par.src, rn); return strings::dup("["): text; - }, - * => abort(), + }; }; const buf = strio::dynamic(); defer io::close(buf); // TODO: Handle invalid syntax here - for (true) match (bufio::scanrune(par.src)) { - io::EOF => break, - rn: rune => switch (rn) { - ']' => { + for (true) { + match (bufio::scanrune(par.src)!) { + case rn: rune => + switch (rn) { + case ']' => bufio::scanrune(par.src) as rune; // ] break; - }, - * => strio::appendrune(buf, rn)!, - }, - * => abort(), + case => + strio::appendrune(buf, rn)!; + }; + case io::EOF => break; + }; }; let id = parse::identstr(strio::string(buf)) as ast::ident; return id: reference; @@ -132,17 +145,21 @@ fn scanref(par: *parser) (token | void) = { fn scansample(par: *parser) (token | void) = { let nws = 0z; - for (true) match (bufio::scanrune(par.src)) { - io::EOF => return, - rn: rune => switch (rn) { - ' ' => nws += 1, - '\t' => nws += 8, - * => { + for (true) { + match (bufio::scanrune(par.src)!) { + case io::EOF => + return; + case rn: rune => + switch (rn) { + case ' ' => + nws += 1; + case '\t' => + nws += 8; + case => bufio::unreadrune(par.src, rn); break; - }, - }, - * => abort(), + }; + }; }; if (nws <= 1) { return scantext(par); @@ -151,37 +168,37 @@ fn scansample(par: *parser) (token | void) = { let cont = true; let buf = strio::dynamic(); for (cont) { - const rn = match (bufio::scanrune(par.src)) { - io::EOF => break, - rn: rune => rn, - * => abort(), + const rn = match (bufio::scanrune(par.src)!) { + case io::EOF => break; + case rn: rune => + yield rn; }; switch (rn) { - * => { - strio::appendrune(buf, rn)!; - continue; - }, - '\n' => strio::appendrune(buf, rn)!, + case '\n' => + strio::appendrune(buf, rn)!; + case => + strio::appendrune(buf, rn)!; + continue; }; // Consume whitespace for (let i = 0z; i < nws) { - match (bufio::scanrune(par.src)) { - io::EOF => break, - * => abort(), - rn: rune => switch (rn) { - ' ' => i += 1, - '\t' => i += 8, - '\n' => { - strio::appendrune(buf, rn)!; - i = 0; - }, - * => { - bufio::unreadrune(par.src, rn); - cont = false; - break; - }, - }, + match (bufio::scanrune(par.src)!) { + case io::EOF => break; + case rn: rune => + switch (rn) { + case ' ' => + i += 1; + case '\t' => + i += 8; + case '\n' => + strio::appendrune(buf, rn)!; + i = 0; + case => + bufio::unreadrune(par.src, rn); + cont = false; + break; + }; }; }; }; @@ -199,16 +216,18 @@ fn scansample(par: *parser) (token | void) = { fn scanlist(par: *parser) (token | void) = { let items: list = []; for (true) { - match (bufio::scanrune(par.src)) { - io::EOF => break, - * => abort(), - rn: rune => if (rn != '-') break, + match (bufio::scanrune(par.src)!) { + case io::EOF => break; + case rn: rune => + if (rn != '-') { + break; + }; }; // XXX: multi-line list items - append(items, match (bufio::scanline(par.src)) { - io::EOF => break, - io::error => abort(), - u: []u8 => strings::fromutf8(u), + append(items, match (bufio::scanline(par.src)!) { + case io::EOF => break; + case u: []u8 => + yield strings::fromutf8(u); }); }; return items; diff --git a/cmd/haredoc/errors.ha b/cmd/haredoc/errors.ha @@ -6,10 +6,17 @@ type eof = void; type syntaxerr = void; type error = !(lex::error | parse::error | io::error | syntaxerr | eof); -fn strerror(err: error) str = match (err) { - err: lex::error => lex::strerror(err), - err: parse::error => parse::strerror(err), - err: io::error => io::strerror(err), - eof => "Unexpected EOF", - syntaxerr => "Syntax error", +fn strerror(err: error) str = { + match (err) { + case err: lex::error => + return lex::strerror(err); + case err: parse::error => + return parse::strerror(err); + case err: io::error => + return io::strerror(err); + case eof => + return "Unexpected EOF"; + case syntaxerr => + return "Syntax error"; + }; }; diff --git a/cmd/haredoc/hare.ha b/cmd/haredoc/hare.ha @@ -14,17 +14,17 @@ fn emit_hare(ctx: *context) (void | error) = { let first = true; match (ctx.readme) { - readme: io::file => { - first = false; - for (true) match (bufio::scanline(&readme)?) { - b: []u8 => { - fmt::printfln("// {}", strings::fromutf8(b))?; - free(b); - }, - io::EOF => break, + case readme: io::file => + first = false; + for (true) { + match (bufio::scanline(&readme)?) { + case io::EOF => break; + case b: []u8 => + fmt::printfln("// {}", strings::fromutf8(b))?; + free(b); }; - }, - void => void, + }; + case void => void; }; // XXX: Should we emit the dependencies, too? @@ -64,11 +64,14 @@ fn details_hare(ctx: *context, decl: ast::decl) (void | error) = { }; const iter = strings::tokenize(decl.docs, "\n"); - for (true) match (strings::next_token(&iter)) { - s: str => if (len(s) != 0) { - fmt::printfln("//{}", s)?; - }, - void => break, + for (true) { + match (strings::next_token(&iter)) { + case void => break; + case s: str => + if (len(s) != 0) { + fmt::printfln("//{}", s)?; + }; + }; }; unparse_hare(os::stdout, decl)?; @@ -80,53 +83,54 @@ fn details_hare(ctx: *context, decl: ast::decl) (void | error) = { fn unparse_hare(out: *io::stream, d: ast::decl) (size | io::error) = { let n = 0z; match (d.decl) { - g: []ast::decl_global => { - n += fmt::fprint(out, - if (g[0].is_const) "def " else "let ")?; - for (let i = 0z; i < len(g); i += 1) { - if (len(g[i].symbol) != 0) { - n += fmt::fprintf(out, - "@symbol(\"{}\") ", g[i].symbol)?; - }; - n += unparse::ident(out, g[i].ident)?; - n += fmt::fprint(out, ": ")?; - n += unparse::_type(out, 0, g[i]._type)?; - if (i + 1 < len(g)) { - n += fmt::fprint(out, ", ")?; - }; - }; - }, - t: []ast::decl_type => { - n += fmt::fprint(out, "type ")?; - for (let i = 0z; i < len(t); i += 1) { - n += unparse::ident(out, t[i].ident)?; - n += fmt::fprint(out, " = ")?; - n += unparse::_type(out, 0, t[i]._type)?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, ", ")?; - }; + case g: []ast::decl_global => + n += fmt::fprint(out, + if (g[0].is_const) "def " else "let ")?; + for (let i = 0z; i < len(g); i += 1) { + if (len(g[i].symbol) != 0) { + n += fmt::fprintf(out, + "@symbol(\"{}\") ", g[i].symbol)?; }; - }, - f: ast::decl_func => { - n += fmt::fprint(out, switch (f.attrs) { - ast::fndecl_attrs::NONE => "", - ast::fndecl_attrs::FINI => "@fini ", - ast::fndecl_attrs::INIT => "@init ", - ast::fndecl_attrs::TEST => "@test ", - })?; - let p = f.prototype.repr as ast::func_type; - if (p.attrs & ast::func_attrs::NORETURN != 0) { - n += fmt::fprint(out, "@noreturn ")?; + n += unparse::ident(out, g[i].ident)?; + n += fmt::fprint(out, ": ")?; + n += unparse::_type(out, 0, g[i]._type)?; + if (i + 1 < len(g)) { + n += fmt::fprint(out, ", ")?; }; - if (len(f.symbol) != 0) { - n += fmt::fprintf(out, "@symbol(\"{}\") ", - f.symbol)?; + }; + case t: []ast::decl_type => + n += fmt::fprint(out, "type ")?; + for (let i = 0z; i < len(t); i += 1) { + n += unparse::ident(out, t[i].ident)?; + n += fmt::fprint(out, " = ")?; + n += unparse::_type(out, 0, t[i]._type)?; + if (i + 1 < len(t)) { + n += fmt::fprint(out, ", ")?; }; - n += fmt::fprint(out, "fn ")?; - n += unparse::ident(out, f.ident)?; - n += prototype_hare(out, 0, - f.prototype.repr as ast::func_type)?; - }, + }; + case f: ast::decl_func => + n += fmt::fprint(out, switch (f.attrs) { + case ast::fndecl_attrs::NONE => + yield ""; + case ast::fndecl_attrs::FINI => + yield "@fini "; + case ast::fndecl_attrs::INIT => + yield "@init "; + case ast::fndecl_attrs::TEST => + yield "@test "; + })?; + let p = f.prototype.repr as ast::func_type; + if (p.attrs & ast::func_attrs::NORETURN != 0) { + n += fmt::fprint(out, "@noreturn ")?; + }; + if (len(f.symbol) != 0) { + n += fmt::fprintf(out, "@symbol(\"{}\") ", + f.symbol)?; + }; + n += fmt::fprint(out, "fn ")?; + n += unparse::ident(out, f.ident)?; + n += prototype_hare(out, 0, + f.prototype.repr as ast::func_type)?; }; n += fmt::fprint(out, ";")?; return n; diff --git a/cmd/haredoc/html.ha b/cmd/haredoc/html.ha @@ -17,16 +17,25 @@ use strio; fn html_escape(out: *io::stream, in: str) (size | io::error) = { let z = 0z; let iter = strings::iter(in); - for (true) match (strings::next(&iter)) { - void => break, - rn: rune => z += io::write(out, switch (rn) { - '&' => strings::toutf8("&amp;"), - '<' => strings::toutf8("&lt;"), - '>' => strings::toutf8("&gt;"), - '"' => strings::toutf8("&quot;"), - '\'' => strings::toutf8("&apos;"), - * => utf8::encoderune(rn), - })?, + for (true) { + match (strings::next(&iter)) { + case void => break; + case rn: rune => + z += io::write(out, switch (rn) { + case '&' => + yield strings::toutf8("&amp;"); + case '<' => + yield strings::toutf8("&lt;"); + case '>' => + yield strings::toutf8("&gt;"); + case '"' => + yield strings::toutf8("&quot;"); + case '\'' => + yield strings::toutf8("&apos;"); + case => + yield utf8::encoderune(rn); + })?; + }; }; return z; }; @@ -63,20 +72,21 @@ fn emit_html(ctx: *context) (void | error) = { }; for (let i = 0z; i < len(ctx.tags); i += 1) { const mode = switch (ctx.tags[i].mode) { - module::tag_mode::INCLUSIVE => '+', - module::tag_mode::EXCLUSIVE => '-', + case module::tag_mode::INCLUSIVE => + yield '+'; + case module::tag_mode::EXCLUSIVE => + yield '-'; }; fmt::printf("{}{} ", mode, ctx.tags[i].name)?; }; fmt::println("</span></h2>")?; match (ctx.readme) { - void => void, - f: io::file => { - fmt::println("<div class='readme'>")?; - markup_html(ctx, &f)?; - fmt::println("</div>")?; - }, + case void => void; + case f: io::file => + fmt::println("<div class='readme'>")?; + markup_html(ctx, &f)?; + fmt::println("</div>")?; }; if (len(ctx.version.subdirs) != 0) { @@ -182,10 +192,14 @@ fn tocentries(decls: []ast::decl, name: str, lname: str) (void | error) = { fn tocentry(decl: ast::decl) (void | error) = { fmt::printf("{} ", match (decl.decl) { - ast::decl_func => "fn", - []ast::decl_type => "type", - []ast::decl_const => "const", - []ast::decl_global => "let", + case ast::decl_func => + yield "fn"; + case []ast::decl_type => + yield "type"; + case []ast::decl_const => + yield "const"; + case []ast::decl_global => + yield "let"; })?; fmt::printf("<a href='#")?; unparse::ident(os::stdout, decl_ident(decl))?; @@ -194,20 +208,19 @@ fn tocentry(decl: ast::decl) (void | error) = { fmt::print("</a>")?; match (decl.decl) { - g: []ast::decl_global => { - let g = g[0]; - fmt::print(": ")?; - type_html(os::stdout, 0, g._type, true)?; - }, - c: []ast::decl_const => { - let c = c[0]; - fmt::print(": ")?; - type_html(os::stdout, 0, c._type, true)?; - }, - t: []ast::decl_type => void, - f: ast::decl_func => prototype_html(os::stdout, 0, + case t: []ast::decl_type => void; + case g: []ast::decl_global => + let g = g[0]; + fmt::print(": ")?; + type_html(os::stdout, 0, g._type, true)?; + case c: []ast::decl_const => + let c = c[0]; + fmt::print(": ")?; + type_html(os::stdout, 0, c._type, true)?; + case f: ast::decl_func => + prototype_html(os::stdout, 0, f.prototype.repr as ast::func_type, - true)?, + true)?; }; fmt::println(";")?; return; @@ -218,17 +231,19 @@ fn details(ctx: *context, decl: ast::decl) (void | error) = { fmt::print("<h4 id='")?; unparse::ident(os::stdout, decl_ident(decl))?; fmt::print("'>")?; - fmt::printf("{} ", - match (decl.decl) { - ast::decl_func => "fn", - []ast::decl_type => "type", - []ast::decl_const => "def", - []ast::decl_global => "let", + fmt::printf("{} ", match (decl.decl) { + case ast::decl_func => + yield "fn"; + case []ast::decl_type => + yield "type"; + case []ast::decl_const => + yield "def"; + case []ast::decl_global => + yield "let"; })?; unparse::ident(os::stdout, decl_ident(decl))?; // TODO: Add source URL - fmt::print("<span class='heading-extra'> - <a href='#")?; + fmt::print("<span class='heading-extra'><a href='#")?; unparse::ident(os::stdout, decl_ident(decl))?; fmt::print("'>[link]</a> </span>")?; @@ -255,8 +270,11 @@ fn details(ctx: *context, decl: ast::decl) (void | error) = { }; fn htmlref(ctx: *context, ref: ast::ident) (void | io::error) = { - const ik = match (resolve(ctx, ref)) { - void => { + const ik = + match (resolve(ctx, ref)) { + case ik: (ast::ident, symkind) => + yield ik; + case void => const ident = unparse::identstr(ref); fmt::errorfln("Warning: Unresolved reference: {}", ident)?; fmt::printf("<a href='#' " @@ -265,28 +283,24 @@ fn htmlref(ctx: *context, ref: ast::ident) (void | io::error) = { ident)?; free(ident); return; - }, - ik: (ast::ident, symkind) => ik, - }; + }; // TODO: The reference is not necessarily in the stdlib const kind = ik.1, id = ik.0; const ident = unparse::identstr(id); switch (kind) { - symkind::LOCAL => - fmt::printf("<a href='#{0}' class='ref'>{0}</a>", ident)?, - symkind::MODULE => { - let ipath = module::identpath(id); - defer free(ipath); - fmt::printf("<a href='/{}' class='ref'>{}</a>", - ipath, ident)?; - }, - symkind::SYMBOL => { - let ipath = module::identpath(id[..len(id) - 1]); - defer free(ipath); - fmt::printf("<a href='/{}#{}' class='ref'>{}</a>", - ipath, id[len(id) - 1], ident)?; - }, + case symkind::LOCAL => + fmt::printf("<a href='#{0}' class='ref'>{0}</a>", ident)?; + case symkind::MODULE => + let ipath = module::identpath(id); + defer free(ipath); + fmt::printf("<a href='/{}' class='ref'>{}</a>", + ipath, ident)?; + case symkind::SYMBOL => + let ipath = module::identpath(id[..len(id) - 1]); + defer free(ipath); + fmt::printf("<a href='/{}#{}' class='ref'>{}</a>", + ipath, id[len(id) - 1], ident)?; }; free(ident); }; @@ -295,15 +309,16 @@ fn markup_html(ctx: *context, in: *io::stream) (void | io::error) = { let parser = parsedoc(in); for (true) { const tok = match (scandoc(&parser)) { - void => break, - tok: token => tok, - }; + case void => break; + case tok: token => + yield tok; + }; match (tok) { - paragraph => { - fmt::println()?; - fmt::print("<p>")?; - }, - tx: text => if (strings::has_prefix(tx, "https://")) { + case paragraph => + fmt::println()?; + fmt::print("<p>")?; + case tx: text => + if (strings::has_prefix(tx, "https://")) { // Temporary hack fmt::print("<a rel='nofollow noopener' href='")?; html_escape(os::stdout, tx)?; @@ -314,23 +329,22 @@ fn markup_html(ctx: *context, in: *io::stream) (void | io::error) = { } else { html_escape(os::stdout, tx)?; free(tx); - }, - re: reference => htmlref(ctx, re)?, - sa: sample => { - fmt::print("<pre class='sample'>")?; - html_escape(os::stdout, sa)?; - fmt::print("</pre>")?; - free(sa); - }, - li: list => { - fmt::println("<ul>")?; - for (let i = 0z; i < len(li); i += 1) { - fmt::println("<li>")?; - html_escape(os::stdout, li[i])?; - fmt::println("</li>")?; - }; - fmt::println("</ul>")?; - }, + }; + case re: reference => + htmlref(ctx, re)?; + case sa: sample => + fmt::print("<pre class='sample'>")?; + html_escape(os::stdout, sa)?; + fmt::print("</pre>")?; + free(sa); + case li: list => + fmt::println("<ul>")?; + for (let i = 0z; i < len(li); i += 1) { + fmt::println("<li>")?; + html_escape(os::stdout, li[i])?; + fmt::println("</li>")?; + }; + fmt::println("</ul>")?; }; }; fmt::println()?; @@ -341,86 +355,108 @@ fn markup_html(ctx: *context, in: *io::stream) (void | io::error) = { fn unparse_html(out: *io::stream, d: ast::decl) (size | io::error) = { let n = 0z; match (d.decl) { - c: []ast::decl_const => { - n += fmt::fprintf(out, "<span class='keyword'>def</span> ")?; - for (let i = 0z; i < len(c); i += 1) { - n += unparse::ident(out, c[i].ident)?; - n += fmt::fprint(out, ": ")?; - n += type_html(out, 0, c[i]._type, false)?; - if (i + 1 < len(c)) { - n += fmt::fprint(out, ", ")?; - }; - }; - }, - g: []ast::decl_global => { - n += fmt::fprintf(out, "<span class='keyword'>{}</span>", - if (g[0].is_const) "const " else "let ")?; - for (let i = 0z; i < len(g); i += 1) { - n += unparse::ident(out, g[i].ident)?; - n += fmt::fprint(out, ": ")?; - n += type_html(out, 0, g[i]._type, false)?; - if (i + 1 < len(g)) { - n += fmt::fprint(out, ", ")?; - }; + case c: []ast::decl_const => + n += fmt::fprintf(out, "<span class='keyword'>def</span> ")?; + for (let i = 0z; i < len(c); i += 1) { + n += unparse::ident(out, c[i].ident)?; + n += fmt::fprint(out, ": ")?; + n += type_html(out, 0, c[i]._type, false)?; + if (i + 1 < len(c)) { + n += fmt::fprint(out, ", ")?; }; - }, - t: []ast::decl_type => { - n += fmt::fprint(out, "<span class='keyword'>type</span> ")?; - for (let i = 0z; i < len(t); i += 1) { - n += unparse::ident(out, t[i].ident)?; - n += fmt::fprint(out, " = ")?; - n += type_html(out, 0, t[i]._type, false)?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, ", ")?; - }; + }; + case g: []ast::decl_global => + n += fmt::fprintf(out, "<span class='keyword'>{}</span>", + if (g[0].is_const) "const " else "let ")?; + for (let i = 0z; i < len(g); i += 1) { + n += unparse::ident(out, g[i].ident)?; + n += fmt::fprint(out, ": ")?; + n += type_html(out, 0, g[i]._type, false)?; + if (i + 1 < len(g)) { + n += fmt::fprint(out, ", ")?; }; - }, - f: ast::decl_func => { - n += fmt::fprint(out, switch (f.attrs) { - ast::fndecl_attrs::NONE => "", - ast::fndecl_attrs::FINI => "@fini ", - ast::fndecl_attrs::INIT => "@init ", - ast::fndecl_attrs::TEST => "@test ", - })?; - let p = f.prototype.repr as ast::func_type; - if (p.attrs & ast::func_attrs::NORETURN != 0) { - n += fmt::fprint(out, "@noreturn ")?; + }; + case t: []ast::decl_type => + n += fmt::fprint(out, "<span class='keyword'>type</span> ")?; + for (let i = 0z; i < len(t); i += 1) { + n += unparse::ident(out, t[i].ident)?; + n += fmt::fprint(out, " = ")?; + n += type_html(out, 0, t[i]._type, false)?; + if (i + 1 < len(t)) { + n += fmt::fprint(out, ", ")?; }; - n += fmt::fprint(out, "<span class='keyword'>fn</span> ")?; - n += unparse::ident(out, f.ident)?; - n += prototype_html(out, 0, - f.prototype.repr as ast::func_type, - false)?; - }, + }; + case f: ast::decl_func => + n += fmt::fprint(out, switch (f.attrs) { + case ast::fndecl_attrs::NONE => + yield ""; + case ast::fndecl_attrs::FINI => + yield "@fini "; + case ast::fndecl_attrs::INIT => + yield "@init "; + case ast::fndecl_attrs::TEST => + yield "@test "; + })?; + let p = f.prototype.repr as ast::func_type; + if (p.attrs & ast::func_attrs::NORETURN != 0) { + n += fmt::fprint(out, "@noreturn ")?; + }; + n += fmt::fprint(out, "<span class='keyword'>fn</span> ")?; + n += unparse::ident(out, f.ident)?; + n += prototype_html(out, 0, + f.prototype.repr as ast::func_type, + false)?; }; n += fmt::fprint(out, ";")?; return n; }; // Forked from [[hare::unparse]]. -fn builtin_type(b: ast::builtin_type) str = switch (b) { - ast::builtin_type::BOOL => "bool", - ast::builtin_type::CHAR => "char", - ast::builtin_type::F32 => "f32", - ast::builtin_type::F64 => "f64", - ast::builtin_type::FCONST => abort("FCONST has no lexical representation"), - ast::builtin_type::I16 => "i16", - ast::builtin_type::I32 => "i32", - ast::builtin_type::I64 => "i64", - ast::builtin_type::I8 => "i8", - ast::builtin_type::ICONST => abort("ICONST has no lexical representation"), - ast::builtin_type::INT => "int", - ast::builtin_type::NULL => "null", - ast::builtin_type::RUNE => "rune", - ast::builtin_type::SIZE => "size", - ast::builtin_type::STR => "str", - ast::builtin_type::U16 => "u16", - ast::builtin_type::U32 => "u32", - ast::builtin_type::U64 => "u64", - ast::builtin_type::U8 => "u8", - ast::builtin_type::UINT => "uint", - ast::builtin_type::UINTPTR => "uintptr", - ast::builtin_type::VOID => "void", +fn builtin_type(b: ast::builtin_type) str = { + switch (b) { + case ast::builtin_type::FCONST, ast::builtin_type::ICONST => + abort("ICONST and FCONST have no lexical representation"); + case ast::builtin_type::BOOL => + return "bool"; + case ast::builtin_type::CHAR => + return "char"; + case ast::builtin_type::F32 => + return "f32"; + case ast::builtin_type::F64 => + return "f64"; + case ast::builtin_type::I16 => + return "i16"; + case ast::builtin_type::I32 => + return "i32"; + case ast::builtin_type::I64 => + return "i64"; + case ast::builtin_type::I8 => + return "i8"; + case ast::builtin_type::INT => + return "int"; + case ast::builtin_type::NULL => + return "null"; + case ast::builtin_type::RUNE => + return "rune"; + case ast::builtin_type::SIZE => + return "size"; + case ast::builtin_type::STR => + return "str"; + case ast::builtin_type::U16 => + return "u16"; + case ast::builtin_type::U32 => + return "u32"; + case ast::builtin_type::U64 => + return "u64"; + case ast::builtin_type::U8 => + return "u8"; + case ast::builtin_type::UINT => + return "uint"; + case ast::builtin_type::UINTPTR => + return "uintptr"; + case ast::builtin_type::VOID => + return "void"; + }; }; // Forked from [[hare::unparse]]. @@ -453,18 +489,16 @@ fn enum_html( z += fmt::fprint(out, val.name)?; match (val.value) { - null => void, - expr: *ast::expr => { - z += fmt::fprint(out, " = ")?; - z += unparse::expr(out, indent, *expr)?; - }, + case null => void; + case expr: *ast::expr => + z += fmt::fprint(out, " = ")?; + z += unparse::expr(out, indent, *expr)?; }; z += fmt::fprint(out, ",")?; }; z += newline(out, indent)?; z += fmt::fprint(out, "}")?; - return z; }; @@ -476,14 +510,12 @@ fn struct_union_html( ) (size | io::error) = { let z = 0z; let members = match (t.repr) { - t: ast::struct_type => { - z += fmt::fprint(out, "<span class='keyword'>struct</span> {")?; - yield t: []ast::struct_member; - }, - t: ast::union_type => { - z += fmt::fprint(out, "<span class='keyword'>union</span> {")?; - yield t: []ast::struct_member; - }, + case t: ast::struct_type => + z += fmt::fprint(out, "<span class='keyword'>struct</span> {")?; + yield t: []ast::struct_member; + case t: ast::union_type => + z += fmt::fprint(out, "<span class='keyword'>union</span> {")?; + yield t: []ast::struct_member; }; indent += 1; @@ -492,25 +524,21 @@ fn struct_union_html( z += newline(out, indent)?; match (member._offset) { - null => void, - expr: *ast::expr => { - z += fmt::fprint(out, "@offset(")?; - z += unparse::expr(out, indent, *expr)?; - z += fmt::fprint(out, ") ")?; - }, + case null => void; + case expr: *ast::expr => + z += fmt::fprint(out, "@offset(")?; + z += unparse::expr(out, indent, *expr)?; + z += fmt::fprint(out, ") ")?; }; match (member.member) { - f: ast::struct_field => { - z += fmt::fprintf(out, "{}: ", f.name)?; - z += type_html(out, indent, *f._type, brief)?; - }, - embed: ast::struct_embedded => { - z += type_html(out, indent, *embed, brief)?; - }, - indent: ast::struct_alias => { - z += unparse::ident(out, indent)?; - }, + case f: ast::struct_field => + z += fmt::fprintf(out, "{}: ", f.name)?; + z += type_html(out, indent, *f._type, brief)?; + case embed: ast::struct_embedded => + z += type_html(out, indent, *embed, brief)?; + case indent: ast::struct_alias => + z += unparse::ident(out, indent)?; }; z += fmt::fprint(out, ",")?; }; @@ -553,85 +581,85 @@ fn type_html( }; match (_type.repr) { - a: ast::alias_type => { - if (a.unwrap) { - z += fmt::fprint(out, "...")?; - }; - z += unparse::ident(out, a.ident)?; - }, - t: ast::builtin_type => { - z += fmt::fprintf(out, "<span class='type'>{}</span>", - builtin_type(t))?; - }, - t: ast::tagged_type => { - z += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - z += type_html(out, indent, *t[i], brief)?; - if (i < len(t) - 1) { - z += fmt::fprint(out, " | ")?; - }; - }; - z += fmt::fprint(out, ")")?; - }, - t: ast::tuple_type => { - z += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - z += type_html(out, indent, *t[i], brief)?; - if (i < len(t) - 1) { - z += fmt::fprint(out, ", ")?; - }; - }; - z += fmt::fprint(out, ")")?; - }, - t: ast::pointer_type => { - if (t.flags & ast::pointer_flags::NULLABLE != 0) { - z += fmt::fprint(out, "<span class='type'>nullable</span> ")?; + case a: ast::alias_type => + if (a.unwrap) { + z += fmt::fprint(out, "...")?; + }; + z += unparse::ident(out, a.ident)?; + case t: ast::builtin_type => + z += fmt::fprintf(out, "<span class='type'>{}</span>", + builtin_type(t))?; + case t: ast::tagged_type => + z += fmt::fprint(out, "(")?; + for (let i = 0z; i < len(t); i += 1) { + z += type_html(out, indent, *t[i], brief)?; + if (i < len(t) - 1) { + z += fmt::fprint(out, " | ")?; }; - z += fmt::fprint(out, "*")?; - z += type_html(out, indent, *t.referent, brief)?; - }, - t: ast::func_type => { - if (t.attrs & ast::func_attrs::NORETURN == ast::func_attrs::NORETURN) { - z += fmt::fprint(out, "@noreturn ")?; + }; + z += fmt::fprint(out, ")")?; + case t: ast::tuple_type => + z += fmt::fprint(out, "(")?; + for (let i = 0z; i < len(t); i += 1) { + z += type_html(out, indent, *t[i], brief)?; + if (i < len(t) - 1) { + z += fmt::fprint(out, ", ")?; }; + }; + z += fmt::fprint(out, ")")?; + case t: ast::pointer_type => + if (t.flags & ast::pointer_flags::NULLABLE != 0) { + z += fmt::fprint(out, "<span class='type'>nullable</span> ")?; + }; + z += fmt::fprint(out, "*")?; + z += type_html(out, indent, *t.referent, brief)?; + case t: ast::func_type => + if (t.attrs & ast::func_attrs::NORETURN == ast::func_attrs::NORETURN) { + z += fmt::fprint(out, "@noreturn ")?; + }; - z += fmt::fprint(out, "<span class='keyword'>fn</span>(")?; - for (let i = 0z; i < len(t.params); i += 1) { - const param = t.params[i]; - z += fmt::fprintf(out, "{}: ", - if (len(param.name) == 0) "_" else param.name)?; - z += type_html(out, indent, *param._type, brief)?; - - if (i + 1 == len(t.params) - && t.variadism == ast::variadism::HARE) { - // TODO: Highlight that as well - z += fmt::fprint(out, "...")?; - }; - if (i + 1 < len(t.params)) { - z += fmt::fprint(out, ", ")?; - }; - }; - if (t.variadism == ast::variadism::C) { - z += fmt::fprint(out, ", ...")?; + z += fmt::fprint(out, "<span class='keyword'>fn</span>(")?; + for (let i = 0z; i < len(t.params); i += 1) { + const param = t.params[i]; + z += fmt::fprintf(out, "{}: ", + if (len(param.name) == 0) "_" else param.name)?; + z += type_html(out, indent, *param._type, brief)?; + + if (i + 1 == len(t.params) + && t.variadism == ast::variadism::HARE) { + // TODO: Highlight that as well + z += fmt::fprint(out, "...")?; }; - z += fmt::fprint(out, ") ")?; - z += type_html(out, indent, *t.result, brief)?; - }, - t: ast::enum_type => z += enum_html(out, indent, t)?, - t: ast::list_type => { - z += fmt::fprint(out, "[")?; - z += match (t.length) { - expr: *ast::expr => unparse::expr(out, indent, *expr)?, - ast::len_slice => 0, - ast::len_unbounded => fmt::fprintf(out, "*")?, - ast::len_contextual => fmt::fprintf(out, "_")?, + if (i + 1 < len(t.params)) { + z += fmt::fprint(out, ", ")?; }; - z += fmt::fprint(out, "]")?; + }; + if (t.variadism == ast::variadism::C) { + z += fmt::fprint(out, ", ...")?; + }; + z += fmt::fprint(out, ") ")?; + z += type_html(out, indent, *t.result, brief)?; + case t: ast::enum_type => + z += enum_html(out, indent, t)?; + case t: ast::list_type => + z += fmt::fprint(out, "[")?; + match (t.length) { + case expr: *ast::expr => + z += unparse::expr(out, indent, *expr)?; + case ast::len_slice => + z += 0; + case ast::len_unbounded => + z += fmt::fprintf(out, "*")?; + case ast::len_contextual => + z += fmt::fprintf(out, "_")?; + }; + z += fmt::fprint(out, "]")?; - z += type_html(out, indent, *t.members, brief)?; - }, - t: ast::struct_type => z += struct_union_html(out, indent, _type, brief)?, - t: ast::union_type => z += struct_union_html(out, indent, _type, brief)?, + z += type_html(out, indent, *t.members, brief)?; + case t: ast::struct_type => + z += struct_union_html(out, indent, _type, brief)?; + case t: ast::union_type => + z += struct_union_html(out, indent, _type, brief)?; }; return z; diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha @@ -48,21 +48,31 @@ export fn main() void = { for (let i = 0z; i < len(cmd.opts); i += 1) { let opt = cmd.opts[i]; switch (opt.0) { - 'F' => fmt = - if (opt.1 == "hare") format::HARE - else if (opt.1 == "tty") format::TTY - else if (opt.1 == "html") format::HTML - else if (opt.1 == "gemtext") format::GEMTEXT - else fmt::fatal("Invalid format {}", opt.1), - 't' => template = false, - 'a' => show_undocumented = true, - * => abort(), + case 'F' => + switch (opt.1) { + case "hare" => + fmt = format::HARE; + case "tty" => + fmt = format::TTY; + case "html" => + fmt = format::HTML; + case "gemtext" => + fmt = format::GEMTEXT; + case => + fmt::fatal("Invalid format {}", opt.1); + }; + case 't' => + template = false; + case 'a' => + show_undocumented = true; + case => abort(); }; }; if (show_undocumented) switch (fmt) { - format::HARE, format::TTY => void, - * => fmt::fatal("Option -a must be used only with -Fhare or -Ftty"), + case format::HARE, format::TTY => void; + case => + fmt::fatal("Option -a must be used only with -Fhare or -Ftty"); }; let decls: []ast::decl = []; @@ -77,24 +87,28 @@ export fn main() void = { const id: ast::ident = if (len(cmd.args) < 1) [] else match (parse::identstr(cmd.args[0])) { - err: parse::error => fmt::fatal(parse::strerror(err)), - id: ast::ident => id, + case err: parse::error => + fmt::fatal(parse::strerror(err)); + case id: ast::ident => + yield id; }; let decl = ""; let dirname = if (len(id) < 2) id else id[..len(id) - 1]; const version = match (module::lookup(&ctx, id)) { - ver: module::version => ver, - err: module::error => match (module::lookup(&ctx, dirname)) { - ver: module::version => { - assert(len(id) >= 2); - decl = id[len(id) - 1]; - yield ver; - }, - err: module::error => fmt::fatal( + case ver: module::version => + yield ver; + case err: module::error => + yield match (module::lookup(&ctx, dirname)) { + case ver: module::version => + assert(len(id) >= 2); + decl = id[len(id) - 1]; + yield ver; + case err: module::error => + fmt::fatal( "Error scanning input module: {}", - module::strerror(err)), - }, + module::strerror(err)); + }; }; for (let i = 0z; i < len(version.inputs); i += 1) { @@ -104,8 +118,10 @@ export fn main() void = { continue; }; match (scan(in.path)) { - u: ast::subunit => append(decls, u.decls...), - err: error => fmt::fatal("Error: {}", strerror(err)), + case u: ast::subunit => + append(decls, u.decls...); + case err: error => + fmt::fatal("Error: {}", strerror(err)); }; }; @@ -113,13 +129,17 @@ export fn main() void = { defer free(rpath); const readme: (io::file | void) = if (decl == "") { yield match (os::open(rpath)) { - err: fs::error => void, - f: io::file => f, + case err: fs::error => + yield void; + case f: io::file => + yield f; }; } else void; + defer match (readme) { - void => void, - f: io::file => io::close(&f), + case void => void; + case f: io::file => + io::close(&f); }; if (decl != "") { @@ -153,48 +173,58 @@ export fn main() void = { show_undocumented = show_undocumented, }; match (emit(&ctx)) { - void => void, - err: error => fmt::fatal("Error: {}", strerror(err)), + case void => void; + case err: error => + fmt::fatal("Error: {}", strerror(err)); }; }; fn has_decl(decl: ast::decl, name: str) bool = { match (decl.decl) { - d: []ast::decl_const => for (let i = 0z; i < len(d); i += 1) { + case d: []ast::decl_const => + for (let i = 0z; i < len(d); i += 1) { if (len(d[i].ident) == 1 && d[i].ident[0] == name) { return true; }; - }, - d: ast::decl_func => - return len(d.ident) == 1 && d.ident[0] == name, - d: []ast::decl_global => for (let i = 0z; i < len(d); i += 1) { + }; + case d: ast::decl_func => + return len(d.ident) == 1 && d.ident[0] == name; + case d: []ast::decl_global => + for (let i = 0z; i < len(d); i += 1) { if (len(d[i].ident) == 1 && d[i].ident[0] == name) { return true; }; - }, - d: []ast::decl_type => for (let i = 0z; i < len(d); i += 1) { + }; + case d: []ast::decl_type => + for (let i = 0z; i < len(d); i += 1) { if (len(d[i].ident) == 1 && d[i].ident[0] == name) { return true; }; - }, + }; }; return false; }; fn scan(path: str) (ast::subunit | error) = { const input = match (os::open(path)) { - f: io::file => f, - err: fs::error => fmt::fatal("Error reading {}: {}", - path, fs::strerror(err)), + case f: io::file => + yield f; + case err: fs::error => + fmt::fatal("Error reading {}: {}", path, fs::strerror(err)); }; defer io::close(&input); const lexer = lex::init(&input, path, lex::flags::COMMENTS); return parse::subunit(&lexer)?; }; -fn emit(ctx: *context) (void | error) = switch (ctx.format) { - format::HARE => emit_hare(ctx), - format::TTY => emit_tty(ctx), - format::HTML => emit_html(ctx)?, - format::GEMTEXT => abort(), // TODO +fn emit(ctx: *context) (void | error) = { + switch (ctx.format) { + case format::HARE => + emit_hare(ctx)?; + case format::TTY => + emit_tty(ctx)?; + case format::HTML => + emit_html(ctx)?; + case format::GEMTEXT => abort(); // TODO + }; }; diff --git a/cmd/haredoc/resolver.ha b/cmd/haredoc/resolver.ha @@ -20,18 +20,16 @@ fn resolve(ctx: *context, what: ast::ident) ((ast::ident, symkind) | void) = { let partial = what[..len(what) - 1]; match (module::lookup(ctx.mctx, partial)) { - ver: module::version => { - return (what, symkind::SYMBOL); - }, - module::error => void, + case ver: module::version => + return (what, symkind::SYMBOL); + case module::error => void; }; }; match (module::lookup(ctx.mctx, what)) { - ver: module::version => { - return (what, symkind::MODULE); - }, - module::error => void, + case ver: module::version => + return (what, symkind::MODULE); + case module::error => void; }; return; @@ -43,8 +41,8 @@ fn is_local(ctx: *context, what: ast::ident) bool = { }; const summary = ctx.summary; - for (let i = 0z; i < len(summary.types); i += 1) { - const name = decl_ident(summary.types[i])[0]; + for (let i = 0z; i < len(summary.constants); i += 1) { + const name = decl_ident(summary.constants[i])[0]; if (name == what[0]) { return true; }; @@ -55,6 +53,12 @@ fn is_local(ctx: *context, what: ast::ident) bool = { return true; }; }; + for (let i = 0z; i < len(summary.types); i += 1) { + const name = decl_ident(summary.types[i])[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]; if (name == what[0]) { diff --git a/cmd/haredoc/sort.ha b/cmd/haredoc/sort.ha @@ -24,53 +24,54 @@ fn sort_decls(decls: []ast::decl) summary = { }; match (decl.decl) { - f: ast::decl_func => append(sorted.funcs, decl), - t: []ast::decl_type => - for (let j = 0z; j < len(t); j += 1) { - let bucket = &sorted.types; - if (t[j]._type.flags & ast::type_flags::ERROR == ast::type_flags::ERROR) { - bucket = &sorted.errors; - }; - append(bucket, ast::decl { - exported = true, - loc = decl.loc, - decl = { - // XXX: Kind of bad - let new: []ast::decl_type = []; - append(new, t[j]); - yield new; - }, - docs = decl.docs, - }); - }, - c: []ast::decl_const => - for (let j = 0z; j < len(c); j += 1) { - append(sorted.constants, ast::decl { - exported = true, - loc = decl.loc, - decl = { - // XXX: Kind of bad - let new: []ast::decl_const = []; - append(new, c[j]); - yield new; - }, - docs = decl.docs, - }); - }, - g: []ast::decl_global => - for (let j = 0z; j < len(g); j += 1) { - append(sorted.globals, ast::decl { - exported = true, - loc = decl.loc, - decl = { - // XXX: Kind of bad - let new: []ast::decl_global = []; - append(new, g[j]); - yield new; - }, - docs = decl.docs, - }); - }, + case f: ast::decl_func => + append(sorted.funcs, decl); + case t: []ast::decl_type => + for (let j = 0z; j < len(t); j += 1) { + let bucket = &sorted.types; + if (t[j]._type.flags & ast::type_flags::ERROR == ast::type_flags::ERROR) { + bucket = &sorted.errors; + }; + append(bucket, ast::decl { + exported = true, + loc = decl.loc, + decl = { + // XXX: Kind of bad + let new: []ast::decl_type = []; + append(new, t[j]); + yield new; + }, + docs = decl.docs, + }); + }; + case c: []ast::decl_const => + for (let j = 0z; j < len(c); j += 1) { + append(sorted.constants, ast::decl { + exported = true, + loc = decl.loc, + decl = { + // XXX: Kind of bad + let new: []ast::decl_const = []; + append(new, c[j]); + yield new; + }, + docs = decl.docs, + }); + }; + case g: []ast::decl_global => + for (let j = 0z; j < len(g); j += 1) { + append(sorted.globals, ast::decl { + exported = true, + loc = decl.loc, + decl = { + // XXX: Kind of bad + let new: []ast::decl_global = []; + append(new, g[j]); + yield new; + }, + docs = decl.docs, + }); + }; }; }; @@ -94,18 +95,18 @@ fn decl_cmp(a: const *void, b: const *void) int = { return ascii::strcmp(id_a[len(id_a) - 1], id_b[len(id_b) - 1]) as int; }; -fn decl_ident(decl: ast::decl) ast::ident = match (decl.decl) { - f: ast::decl_func => f.ident, - t: []ast::decl_type => { +fn decl_ident(decl: ast::decl) ast::ident = { + match (decl.decl) { + case f: ast::decl_func => + return f.ident; + case t: []ast::decl_type => assert(len(t) == 1); - yield t[0].ident; - }, - c: []ast::decl_const => { + return t[0].ident; + case c: []ast::decl_const => assert(len(c) == 1); - yield c[0].ident; - }, - g: []ast::decl_global => { + return c[0].ident; + case g: []ast::decl_global => assert(len(g) == 1); - yield g[0].ident; - }, + return g[0].ident; + }; }; diff --git a/cmd/haredoc/tty.ha b/cmd/haredoc/tty.ha @@ -16,19 +16,17 @@ fn emit_tty(ctx: *context) (void | error) = { const summary = ctx.summary; match (ctx.readme) { - readme: io::file => { - for (true) match (bufio::scanline(&readme)?) { - b: []u8 => { - firstline = false; - fmt::printfln( - "\x1b[1m" "// {}" "\x1b[0m", - strings::fromutf8(b))?; - free(b); - }, - io::EOF => break, - }; - }, - void => void, + case readme: io::file => + for (true) match (bufio::scanline(&readme)?) { + case io::EOF => break; + case b: []u8 => + firstline = false; + fmt::printfln( + "\x1b[1m" "// {}" "\x1b[0m", + strings::fromutf8(b))?; + free(b); + }; + case void => void; }; // XXX: Should we emit the dependencies, too? @@ -48,11 +46,14 @@ fn emit_tty(ctx: *context) (void | error) = { fn isws(s: str) bool = { const iter = strings::iter(s); - for (true) match (strings::next(&iter)) { - r: rune => if (!ascii::isspace(r)) { - return false; - }, - void => break, + for (true) { + match (strings::next(&iter)) { + case r: rune => + if (!ascii::isspace(r)) { + return false; + }; + case void => break; + }; }; return true; }; @@ -68,11 +69,14 @@ fn details_tty(ctx: *context, decl: ast::decl) (void | error) = { firstline = false; const iter = strings::tokenize(decl.docs, "\n"); - for (true) match (strings::next_token(&iter)) { - s: str => if (!(strings::peek_token(&iter) is void)) { - fmt::printfln("\x1b[1m" "//{}" "\x1b[0m", s)?; - }, - void => break, + for (true) { + match (strings::next_token(&iter)) { + case s: str => + if (!(strings::peek_token(&iter) is void)) { + fmt::printfln("\x1b[1m" "//{}" "\x1b[0m", s)?; + }; + case void => break; + }; }; unparse_tty(os::stdout, decl)?; @@ -84,61 +88,62 @@ fn details_tty(ctx: *context, decl: ast::decl) (void | error) = { fn unparse_tty(out: *io::stream, d: ast::decl) (size | io::error) = { let n = 0z; match (d.decl) { - g: []ast::decl_global => { - n += fmt::fprint(out, "\x1b[34m")?; - n += fmt::fprint(out, - if (g[0].is_const) "def " else "let ")?; - n += fmt::fprint(out, "\x1b[0m")?; - for (let i = 0z; i < len(g); i += 1) { - if (len(g[i].symbol) != 0) { - n += fmt::fprintf(out, - "\x1b[33m" "@symbol(\"{}\") " "\x1b[0m", - g[i].symbol)?; - }; - n += unparse::ident(out, g[i].ident)?; - n += fmt::fprint(out, ": ")?; - n += type_tty(out, 0, g[i]._type)?; - if (i + 1 < len(g)) { - n += fmt::fprint(out, ", ")?; - }; - }; - }, - t: []ast::decl_type => { - n += fmt::fprint(out, "\x1b[34m" "type " "\x1b[0m")?; - for (let i = 0z; i < len(t); i += 1) { - n += unparse::ident(out, t[i].ident)?; - n += fmt::fprint(out, " = ")?; - n += type_tty(out, 0, t[i]._type)?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, ", ")?; - }; - }; - }, - f: ast::decl_func => { - n += fmt::fprint(out, "\x1b[33m")?; - n += fmt::fprint(out, switch (f.attrs) { - ast::fndecl_attrs::NONE => "", - ast::fndecl_attrs::FINI => "@fini ", - ast::fndecl_attrs::INIT => "@init ", - ast::fndecl_attrs::TEST => "@test ", - })?; - n += fmt::fprint(out, "\x1b[0m")?; - - let p = f.prototype.repr as ast::func_type; - if (p.attrs & ast::func_attrs::NORETURN != 0) { - n += fmt::fprint(out, - "\x1b[33m" "@noreturn " "\x1b[0m")?; - }; - if (len(f.symbol) != 0) { + case g: []ast::decl_global => + n += fmt::fprint(out, "\x1b[34m")?; + n += fmt::fprint(out, + if (g[0].is_const) "def " else "let ")?; + n += fmt::fprint(out, "\x1b[0m")?; + for (let i = 0z; i < len(g); i += 1) { + if (len(g[i].symbol) != 0) { n += fmt::fprintf(out, "\x1b[33m" "@symbol(\"{}\") " "\x1b[0m", - f.symbol)?; + g[i].symbol)?; + }; + n += unparse::ident(out, g[i].ident)?; + n += fmt::fprint(out, ": ")?; + n += type_tty(out, 0, g[i]._type)?; + if (i + 1 < len(g)) { + n += fmt::fprint(out, ", ")?; + }; + }; + case t: []ast::decl_type => + n += fmt::fprint(out, "\x1b[34m" "type " "\x1b[0m")?; + for (let i = 0z; i < len(t); i += 1) { + n += unparse::ident(out, t[i].ident)?; + n += fmt::fprint(out, " = ")?; + n += type_tty(out, 0, t[i]._type)?; + if (i + 1 < len(t)) { + n += fmt::fprint(out, ", ")?; }; - n += fmt::fprint(out, "\x1b[34m" "fn " "\x1b[0m")?; - n += unparse::ident(out, f.ident)?; - n += prototype_tty(out, 0, - f.prototype.repr as ast::func_type)?; - }, + }; + case f: ast::decl_func => + n += fmt::fprint(out, "\x1b[33m")?; + n += fmt::fprint(out, switch (f.attrs) { + case ast::fndecl_attrs::NONE => + yield ""; + case ast::fndecl_attrs::FINI => + yield "@fini "; + case ast::fndecl_attrs::INIT => + yield "@init "; + case ast::fndecl_attrs::TEST => + yield "@test "; + })?; + n += fmt::fprint(out, "\x1b[0m")?; + + let p = f.prototype.repr as ast::func_type; + if (p.attrs & ast::func_attrs::NORETURN != 0) { + n += fmt::fprint(out, + "\x1b[33m" "@noreturn " "\x1b[0m")?; + }; + if (len(f.symbol) != 0) { + n += fmt::fprintf(out, + "\x1b[33m" "@symbol(\"{}\") " "\x1b[0m", + f.symbol)?; + }; + n += fmt::fprint(out, "\x1b[34m" "fn " "\x1b[0m")?; + n += unparse::ident(out, f.ident)?; + n += prototype_tty(out, 0, + f.prototype.repr as ast::func_type)?; }; n += fmt::fprint(out, ";")?; return n; @@ -182,36 +187,36 @@ fn struct_union_type_tty( ) (size | io::error) = { let z = 0z; let membs = match (t.repr) { - st: ast::struct_type => { - z += fmt::fprint(out, - "\x1b[36m" "struct" "\x1b[0m" " {")?; - yield st: []ast::struct_member; - }, - ut: ast::union_type => { - z += fmt::fprint(out, - "\x1b[36m" "union" "\x1b[0m" " {")?; - yield ut: []ast::struct_member; - }, + case st: ast::struct_type => + z += fmt::fprint(out, + "\x1b[36m" "struct" "\x1b[0m" " {")?; + yield st: []ast::struct_member; + case ut: ast::union_type => + z += fmt::fprint(out, + "\x1b[36m" "union" "\x1b[0m" " {")?; + yield ut: []ast::struct_member; }; indent += 1z; for (let i = 0z; i < len(membs); i += 1) { z += newline(out, indent)?; - z += match (membs[i]._offset) { - null => 0z, - ex: *ast::expr => fmt::fprint(out, "\x1b[33m" "@offset(")? - + unparse::expr(out, indent, *ex)? - + fmt::fprint(out, ") \x1b[0m")?, + match (membs[i]._offset) { + case null => void; + case ex: *ast::expr => + z += fmt::fprint(out, "\x1b[33m" "@offset(")?; + z += unparse::expr(out, indent, *ex)?; + z += fmt::fprint(out, ") \x1b[0m")?; }; - z += match (membs[i].member) { - se: ast::struct_embedded => type_tty(out, indent, *se)?, - sa: ast::struct_alias => unparse::ident(out, sa)?, - sf: ast::struct_field => { - yield fmt::fprintf(out, "{}: ", sf.name)? - + type_tty(out, indent, *sf._type)?; - }, + match (membs[i].member) { + case se: ast::struct_embedded => + z += type_tty(out, indent, *se)?; + case sa: ast::struct_alias => + z += unparse::ident(out, sa)?; + case sf: ast::struct_field => + z += fmt::fprintf(out, "{}: ", sf.name)?; + z += type_tty(out, indent, *sf._type)?; }; z += fmt::fprint(out, ",")?; @@ -243,91 +248,87 @@ fn type_tty( }; match (t.repr) { - a: ast::alias_type => { - if (a.unwrap) { - n += fmt::fprint(out, "...")?; - }; - n += unparse::ident(out, a.ident)?; - }, - b: ast::builtin_type => { - n += fmt::fprintf(out, "\x1b[36m" "{}" "\x1b[0m", - builtin_type(b))?; - }, - e: ast::enum_type => { - n += fmt::fprint(out, "\x1b[36m" "enum " "\x1b[0m")?; - if (e.storage != ast::builtin_type::INT) { - n += fmt::fprintf(out, - "\x1b[36m" "{}" "\x1b[0m", - builtin_type(e.storage))?; - }; - n += fmt::fprint(out, " {")?; - indent += 1; - for (let i = 0z; i < len(e.values); i += 1) { - n += newline(out, indent)?; - let value = e.values[i]; - n += fmt::fprint(out, value.name)?; - match (value.value) { - null => void, - e: *ast::expr => { - n += fmt::fprint(out, " = ")?; - n += unparse::expr(out, indent, *e)?; - }, - }; - n += fmt::fprint(out, ",")?; - }; - indent -= 1; + case a: ast::alias_type => + if (a.unwrap) { + n += fmt::fprint(out, "...")?; + }; + n += unparse::ident(out, a.ident)?; + case b: ast::builtin_type => + n += fmt::fprintf(out, "\x1b[36m" "{}" "\x1b[0m", + builtin_type(b))?; + case e: ast::enum_type => + n += fmt::fprint(out, "\x1b[36m" "enum " "\x1b[0m")?; + if (e.storage != ast::builtin_type::INT) { + n += fmt::fprintf(out, + "\x1b[36m" "{}" "\x1b[0m", + builtin_type(e.storage))?; + }; + n += fmt::fprint(out, " {")?; + indent += 1; + for (let i = 0z; i < len(e.values); i += 1) { n += newline(out, indent)?; - n += fmt::fprint(out, "}")?; - }, - f: ast::func_type => { - if (f.attrs & ast::func_attrs::NORETURN != 0) { - n += fmt::fprint(out, - "\x1b[33m" "@noreturn " "\x1b[0m")?; - }; - n += fmt::fprint(out, "\x1b[34m" "fn" "\x1b[0m")?; - n += prototype_tty(out, indent, f)?; - }, - l: ast::list_type => { - n += fmt::fprint(out, "[")?; - n += match (l.length) { - ast::len_slice => 0, - ast::len_unbounded => fmt::fprint(out, "*")?, - ast::len_contextual => fmt::fprint(out, "_")?, - e: *ast::expr => unparse::expr(out, indent, *e)?, - }; - n += fmt::fprint(out, "]")?; - n += type_tty(out, indent, *l.members)?; - }, - p: ast::pointer_type => { - if (p.flags & ast::pointer_flags::NULLABLE != 0) { - n += fmt::fprint(out, - "\x1b[36m" "nullable " "\x1b[0m")?; + let value = e.values[i]; + n += fmt::fprint(out, value.name)?; + match (value.value) { + case null => void; + case e: *ast::expr => + n += fmt::fprint(out, " = ")?; + n += unparse::expr(out, indent, *e)?; }; + n += fmt::fprint(out, ",")?; + }; + indent -= 1; + n += newline(out, indent)?; + n += fmt::fprint(out, "}")?; + case f: ast::func_type => + if (f.attrs & ast::func_attrs::NORETURN != 0) { + n += fmt::fprint(out, + "\x1b[33m" "@noreturn " "\x1b[0m")?; + }; + n += fmt::fprint(out, "\x1b[34m" "fn" "\x1b[0m")?; + n += prototype_tty(out, indent, f)?; + case l: ast::list_type => + n += fmt::fprint(out, "[")?; + match (l.length) { + case ast::len_slice => void; + case ast::len_unbounded => n += fmt::fprint(out, "*")?; - n += type_tty(out, indent, *p.referent)?; - }, - ast::struct_type => n += struct_union_type_tty(out, indent, t)?, - ast::union_type => n += struct_union_type_tty(out, indent, t)?, - t: ast::tagged_type => { - n += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - n += type_tty(out, indent, *t[i])?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, " | ")?; - }; + case ast::len_contextual => + n += fmt::fprint(out, "_")?; + case e: *ast::expr => + n += unparse::expr(out, indent, *e)?; + }; + n += fmt::fprint(out, "]")?; + n += type_tty(out, indent, *l.members)?; + case p: ast::pointer_type => + if (p.flags & ast::pointer_flags::NULLABLE != 0) { + n += fmt::fprint(out, + "\x1b[36m" "nullable " "\x1b[0m")?; + }; + n += fmt::fprint(out, "*")?; + n += type_tty(out, indent, *p.referent)?; + case ast::struct_type => + n += struct_union_type_tty(out, indent, t)?; + case ast::union_type => + n += struct_union_type_tty(out, indent, t)?; + case t: ast::tagged_type => + n += fmt::fprint(out, "(")?; + for (let i = 0z; i < len(t); i += 1) { + n += type_tty(out, indent, *t[i])?; + if (i + 1 < len(t)) { + n += fmt::fprint(out, " | ")?; }; - n += fmt::fprint(out, ")")?; - }, - t: ast::tuple_type => { - n += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - n += type_tty(out, indent, *t[i])?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, ", ")?; - }; + }; + n += fmt::fprint(out, ")")?; + case t: ast::tuple_type => + n += fmt::fprint(out, "(")?; + for (let i = 0z; i < len(t); i += 1) { + n += type_tty(out, indent, *t[i])?; + if (i + 1 < len(t)) { + n += fmt::fprint(out, ", ")?; }; - n += fmt::fprint(out, ")")?; - }, + }; + n += fmt::fprint(out, ")")?; }; return n; }; diff --git a/compress/flate/inflate.ha b/compress/flate/inflate.ha @@ -102,10 +102,12 @@ fn bits(d: *decompressor, want: u32) (u32 | io::error) = { for (d.cnt < want) { let buf: [_]u8 = [0]; match (io::read(d.in, buf)?) { - io::EOF => return wraperror(inflate_err::EOF), - z: size => if (z < 1) { + case io::EOF => + return wraperror(inflate_err::EOF); + case z: size => + if (z < 1) { continue; // Short read, retry - }, + }; }; val |= buf[0] << d.cnt; d.cnt += 8; @@ -229,11 +231,11 @@ fn uncompressed_read(d: *decompressor) (void | io::EOF | io::error) = { static let _buf: [1024]u8 = [0...]; let buf = if (len(_buf) > d.left: size) _buf[..d.left] else _buf[..]; let z = match (io::read(d.in, buf)?) { - z: size => { - d.left -= z; - yield z; - }, - io::EOF => return wraperror(inflate_err::EOF), + case z: size => + d.left -= z; + yield z; + case io::EOF => + return wraperror(inflate_err::EOF); }; put(d, buf[..z]...); }; @@ -241,13 +243,20 @@ fn uncompressed_read(d: *decompressor) (void | io::EOF | io::error) = { fn opaque_strerror( data: *errors::opaque_data ) const str = switch (*(data: *inflate_err)) { - inflate_err::EOF => "Unexpected EOF", - inflate_err::BTYPE => "Invalid block type", - inflate_err::LEN => "Invalid block length", - inflate_err::DISTANCE => "Invalid distance", - inflate_err::CODE => "Invalid Huffman code", - inflate_err::HUFFMAN => "Oversubscribed Huffman code", - inflate_err::TABLE => "Invalid dynamic Huffman code table", +case inflate_err::EOF => + yield "Unexpected EOF"; +case inflate_err::BTYPE => + yield "Invalid block type"; +case inflate_err::LEN => + yield "Invalid block length"; +case inflate_err::DISTANCE => + yield "Invalid distance"; +case inflate_err::CODE => + yield "Invalid Huffman code"; +case inflate_err::HUFFMAN => + yield "Oversubscribed Huffman code"; +case inflate_err::TABLE => + yield "Invalid dynamic Huffman code table"; }; fn wraperror(err: inflate_err) errors::opaque = { @@ -287,16 +296,18 @@ fn dynamic(d: *decompressor) (void | io::EOF | io::error) = { }; let n = 0u16; let times = switch (c) { - 16 => { - if (len(lens) == 0) { - return wraperror(inflate_err::TABLE); - }; - n = lens[len(lens) - 1]; - yield 3 + bits(d, 2)?; - }, - 17 => 3 + bits(d, 3)?, - 18 => 11 + bits(d, 7)?, - * => abort(), + case 16 => + if (len(lens) == 0) { + return wraperror(inflate_err::TABLE); + }; + n = lens[len(lens) - 1]; + yield 3 + bits(d, 2)?; + case 17 => + yield 3 + bits(d, 3)?; + case 18 => + yield 11 + bits(d, 7)?; + case => + abort(); }; for (times > 0; times -= 1) { append(lens, n); @@ -318,34 +329,37 @@ fn next(d: *decompressor) (void | io::EOF | io::error) = { if (bits(d, 1)? == 1) { d.final = true; }; - return switch (bits(d, 2)?) { - 0b00 => { - // Skip to next byte boundary - d.cnt = 0; - d.bitbuf = 0; - let buf: [4]u8 = [0...]; - for (let n = 0z; n < 4) { - match (io::read(d.in, buf[n..])?) { - io::EOF => return wraperror(inflate_err::EOF), - z: size => n += z, - }; - }; - const length = endian::legetu16(buf[..2]); - if (length != ~endian::legetu16(buf[2..])) { - return wraperror(inflate_err::LEN); + switch (bits(d, 2)?) { + case 0b00 => + // Skip to next byte boundary + d.cnt = 0; + d.bitbuf = 0; + let buf: [4]u8 = [0...]; + for (let n = 0z; n < 4) { + match (io::read(d.in, buf[n..])?) { + case io::EOF => + return wraperror(inflate_err::EOF); + case z: size => + n += z; }; - d.state = &uncompressed_read; - d.left = length; - }, - 0b01 => { - d.hl = fixed_len; - d.hd = fixed_dist; - d.state = &compressed_read; - d.left = 1; - }, - 0b10 => dynamic(d), - 0b11 => wraperror(inflate_err::BTYPE), - * => abort(), + }; + const length = endian::legetu16(buf[..2]); + if (length != ~endian::legetu16(buf[2..])) { + return wraperror(inflate_err::LEN); + }; + d.state = &uncompressed_read; + d.left = length; + case 0b01 => + d.hl = fixed_len; + d.hd = fixed_dist; + d.state = &compressed_read; + d.left = 1; + case 0b10 => + return dynamic(d); + case 0b11 => + return wraperror(inflate_err::BTYPE); + case => + abort(); }; }; @@ -356,12 +370,14 @@ fn read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { for (n < len(buf)) { if (len(s.buf) == 0) { if (s.left == 0) match (next(s)?) { - void => void, - io::EOF => return if (n == 0) io::EOF else n, + case void => void; + case io::EOF => + return if (n == 0) io::EOF else n; }; match (s.state(s)?) { - void => void, - io::EOF => return if (n == 0) io::EOF else n, + case void => void; + case io::EOF => + return if (n == 0) io::EOF else n; }; }; const toread = @@ -411,11 +427,10 @@ fn close(s: *io::stream) void = { let s = inflate(ins); defer io::close(&s); match (io::copy(outs, &s)) { - size => void, - e: io::error => { - fmt::errorln(io::strerror(e))!; - abort(); - }, + case size => void; + case e: io::error => + fmt::errorln(io::strerror(e))!; + abort(); }; let out = bufio::finish(outs); defer free(out); diff --git a/compress/zlib/reader.ha b/compress/zlib/reader.ha @@ -32,11 +32,16 @@ type decompress_err = enum { fn opaque_strerror( data: *errors::opaque_data ) const str = switch (*(data: *decompress_err)) { - decompress_err::HEADER => "Invalid zlib header", - decompress_err::CHECKSUM => "Invalid zlib checksum", - decompress_err::EOF => "Unexpected EOF", - decompress_err::DICT => "Invalid dictionary", - * => abort(), +case decompress_err::HEADER => + yield "Invalid zlib header"; +case decompress_err::CHECKSUM => + yield "Invalid zlib checksum"; +case decompress_err::EOF => + yield "Unexpected EOF"; +case decompress_err::DICT => + yield "Invalid dictionary"; +case => + abort(); }; fn wraperror(err: decompress_err) errors::opaque = { @@ -52,8 +57,10 @@ fn verifysum(s: *reader) (io::EOF | io::error) = { for (let n = 0z; n < len(hash)) { match (io::read(s.source, hash[n..])?) { - io::EOF => return wraperror(decompress_err::EOF), - z: size => n += z, + case io::EOF => + return wraperror(decompress_err::EOF); + case z: size => + n += z; }; }; @@ -64,8 +71,10 @@ fn verifysum(s: *reader) (io::EOF | io::error) = { fn read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { let s = s: *reader; match (io::read(&s.flate, buf)?) { - io::EOF => return verifysum(s), - z: size => buf = buf[..z], + case io::EOF => + return verifysum(s); + case z: size => + buf = buf[..z]; }; return hash::write(&s.hash, buf); }; @@ -80,8 +89,10 @@ export fn decompress(s: *io::stream) (reader | io::error) = { let buf: [2]u8 = [0...]; for (let n = 0z; n < len(buf)) { match (io::read(s, buf[n..])?) { - io::EOF => return wraperror(decompress_err::EOF), - z: size => n += z, + case io::EOF => + return wraperror(decompress_err::EOF); + case z: size => + n += z; }; }; if (buf[0] & CM != 8) { @@ -116,18 +127,17 @@ export fn decompress(s: *io::stream) (reader | io::error) = { let in = bufio::fixed(*vectors[i].1, io::mode::READ); let out = bufio::dynamic(io::mode::WRITE); let d = match (decompress(in)) { - s: reader => s, - e: io::error => { - fmt::errorln(io::strerror(e))!; - abort(); - }, + case s: reader => + yield s; + case e: io::error => + fmt::errorln(io::strerror(e))!; + abort(); }; match (io::copy(out, &d)) { - size => void, - e: io::error => { - fmt::errorfln("vector {}: {}", i, io::strerror(e))!; - abort(); - }, + case size => void; + case e: io::error => + fmt::errorfln("vector {}: {}", i, io::strerror(e))!; + abort(); }; let s = bufio::finish(out); assert(bytes::equal(s, *vectors[i].0)); diff --git a/crypto/random/+linux.ha b/crypto/random/+linux.ha @@ -10,19 +10,24 @@ export fn buffer(buf: []u8) void = { let n = 0z; for (n < len(buf)) { match (rt::getrandom(buf[n..]: *[*]u8, len(buf), 0)) { - err: rt::errno => switch (err) { - rt::EINTR => void, - * => abort(), - }, - z: size => n += z, + case err: rt::errno => + switch (err) { + case rt::EINTR => void; + case => + abort(); + }; + case z: size => + n += z; }; }; }; fn rand_reader(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { assert(s == stream); - return match (rt::getrandom(buf: *[*]u8, len(buf), 0)) { - err: rt::errno => errors::errno(err), - n: size => n, + match (rt::getrandom(buf: *[*]u8, len(buf), 0)) { + case err: rt::errno => + return errors::errno(err); + case n: size => + return n; }; }; diff --git a/crypto/random/random.ha b/crypto/random/random.ha @@ -27,8 +27,10 @@ export let stream: *io::stream = &_stream; let buf: [4096]u8 = [0...]; let test: []u8 = []; match (io::read(stream, buf[..])) { - (io::error | io::EOF) => abort(), - n: size => test = buf[..n], + case (io::error | io::EOF) => + abort(); + case n: size => + test = buf[..n]; }; assert(len(test) > 0); diff --git a/crypto/sha512/sha512.ha b/crypto/sha512/sha512.ha @@ -167,46 +167,42 @@ fn sum(h: *hash::hash, buf: []u8) void = { fn reset(h: *hash::hash) void = { let d = h: *digest; switch (d.var) { - variant::SHA384 => { - d.h[0] = init0_384; - d.h[1] = init1_384; - d.h[2] = init2_384; - d.h[3] = init3_384; - d.h[4] = init4_384; - d.h[5] = init5_384; - d.h[6] = init6_384; - d.h[7] = init7_384; - }, - variant::SHA512_224 => { - d.h[0] = init0_224; - d.h[1] = init1_224; - d.h[2] = init2_224; - d.h[3] = init3_224; - d.h[4] = init4_224; - d.h[5] = init5_224; - d.h[6] = init6_224; - d.h[7] = init7_224; - }, - variant::SHA512_256 => { - d.h[0] = init0_256; - d.h[1] = init1_256; - d.h[2] = init2_256; - d.h[3] = init3_256; - d.h[4] = init4_256; - d.h[5] = init5_256; - d.h[6] = init6_256; - d.h[7] = init7_256; - }, - * => { - d.h[0] = init0; - d.h[1] = init1; - d.h[2] = init2; - d.h[3] = init3; - d.h[4] = init4; - d.h[5] = init5; - d.h[6] = init6; - d.h[7] = init7; - } + case variant::SHA384 => + d.h[0] = init0_384; + d.h[1] = init1_384; + d.h[2] = init2_384; + d.h[3] = init3_384; + d.h[4] = init4_384; + d.h[5] = init5_384; + d.h[6] = init6_384; + d.h[7] = init7_384; + case variant::SHA512_224 => + d.h[0] = init0_224; + d.h[1] = init1_224; + d.h[2] = init2_224; + d.h[3] = init3_224; + d.h[4] = init4_224; + d.h[5] = init5_224; + d.h[6] = init6_224; + d.h[7] = init7_224; + case variant::SHA512_256 => + d.h[0] = init0_256; + d.h[1] = init1_256; + d.h[2] = init2_256; + d.h[3] = init3_256; + d.h[4] = init4_256; + d.h[5] = init5_256; + d.h[6] = init6_256; + d.h[7] = init7_256; + case => + d.h[0] = init0; + d.h[1] = init1; + d.h[2] = init2; + d.h[3] = init3; + d.h[4] = init4; + d.h[5] = init5; + d.h[6] = init6; + d.h[7] = init7; }; d.nx = 0; d.ln = 0; diff --git a/dirs/xdg.ha b/dirs/xdg.ha @@ -5,26 +5,23 @@ use io; fn lookup(prog: str, var: str, default: str) str = { match (os::getenv(var)) { - s: str => { - let path = path::join(s, prog); - match (os::stat(path)) { - err: fs::error => { - os::mkdirs(path) as void; - return path; - }, - st: fs::filestat => { - if (fs::isdir(st.mode)) { - return path; - }; - }, + case s: str => + const path = path::join(s, prog); + match (os::stat(path)) { + case err: fs::error => + os::mkdirs(path)!; + return path; + case st: fs::filestat => + if (fs::isdir(st.mode)) { + return path; }; - }, - void => void, + }; + case void => void; }; let home = os::getenv("HOME") as str; let path = path::join(home, default, prog); - os::mkdirs(path) as void; + os::mkdirs(path)!; return path; }; diff --git a/encoding/base64/base64.ha b/encoding/base64/base64.ha @@ -118,61 +118,63 @@ export fn decode( for (true) { let buf: [4]u8 = [0...]; match (io::read(in, buf)) { - size => { - for (let i = 0z; i < 2; i += 1) { - if (decoder[buf[i]] == INVALID_OR_PAD) { - return (count + i): invalid; - } else { - buf[i] = decoder[buf[i]]; - }; - }; - - if (decoder[buf[2]] == INVALID_OR_PAD) { - if (buf[2] != PADDING) { - return (count + 2z): invalid; - }; - if (buf[3] != PADDING) { - return (count + 3z): invalid; - }; - z += io::write(out, [ - buf[0] << 2 | buf[1] >> 4, - ])?; - let extra: []u8 = [0]; - return match (io::read(in, extra)) { - size => (count + 4z): invalid, - io::EOF => z, - }; + case size => + for (let i = 0z; i < 2; i += 1) { + if (decoder[buf[i]] == INVALID_OR_PAD) { + return (count + i): invalid; } else { - buf[2] = decoder[buf[2]]; + buf[i] = decoder[buf[i]]; }; + }; - if (decoder[buf[3]] == INVALID_OR_PAD) { - if (buf[3] != PADDING) { - return (count + 3z): invalid; - }; - z += io::write(out, [ - buf[0] << 2 | buf[1] >> 4, - buf[1] << 4 | buf[2] >> 2, - ])?; - let extra: []u8 = [0]; - return match (io::read(in, extra)) { - size => (count + 4z): invalid, - io::EOF => z, - }; - } else { - buf[3] = decoder[buf[3]]; + if (decoder[buf[2]] == INVALID_OR_PAD) { + if (buf[2] != PADDING) { + return (count + 2z): invalid; + }; + if (buf[3] != PADDING) { + return (count + 3z): invalid; }; + z += io::write(out, [ + buf[0] << 2 | buf[1] >> 4, + ])?; + let extra: []u8 = [0]; + match (io::read(in, extra)) { + case size => + return (count + 4z): invalid; + case io::EOF => + return z; + }; + } else { + buf[2] = decoder[buf[2]]; + }; + if (decoder[buf[3]] == INVALID_OR_PAD) { + if (buf[3] != PADDING) { + return (count + 3z): invalid; + }; z += io::write(out, [ buf[0] << 2 | buf[1] >> 4, buf[1] << 4 | buf[2] >> 2, - buf[2] << 6 | buf[3], ])?; - count += 4; - }, - io::EOF => { - break; - }, + let extra: []u8 = [0]; + match (io::read(in, extra)) { + case size => + return (count + 4z): invalid; + case io::EOF => + return z; + }; + } else { + buf[3] = decoder[buf[3]]; + }; + + z += io::write(out, [ + buf[0] << 2 | buf[1] >> 4, + buf[1] << 4 | buf[2] >> 2, + buf[2] << 6 | buf[3], + ])?; + count += 4; + case io::EOF => + break; }; }; return z; @@ -187,10 +189,13 @@ export fn decode_static( ) (size | invalid) = { let buf = bufio::fixed(out, io::mode::WRITE); defer io::close(buf); - return match (decode(alphabet, in, buf)) { - io::error => abort(), - z: invalid => z: invalid, - z: size => z, + match (decode(alphabet, in, buf)) { + case io::error => + abort(); + case z: invalid => + return z: invalid; + case z: size => + return z; }; }; @@ -218,10 +223,14 @@ export fn decodeslice(alphabet: []u8, in: []u8) ([]u8 | invalid) = { let out = bufio::dynamic(io::mode::WRITE); let in = bufio::fixed(in, io::mode::READ); defer io::close(in); - return match (decode(alphabet, in, out)) { - io::error => abort(), - z: invalid => z: invalid, - size => bufio::finish(out), + match (decode(alphabet, in, out)) { + case io::error => + abort(); + case z: invalid => + io::close(out); + return z: invalid; + case size => + return bufio::finish(out); }; }; @@ -253,7 +262,7 @@ export fn decodeslice_static( defer free(s); assert(bytes::equal(s, expect[..i])); }; - + const bad: [_]str = [ "A", "AA", diff --git a/encoding/hex/hex.ha b/encoding/hex/hex.ha @@ -49,8 +49,10 @@ export fn decode(s: str) ([]u8 | invalid) = { for (let i = 0z; i < len(s) / 2; i += 1) { let oct = strings::fromutf8_unsafe(s[i * 2..i * 2 + 2]); let u = match (strconv::stou8b(oct, 16)) { - (strconv::invalid | strconv::overflow) => return invalid, - u: u8 => u, + case (strconv::invalid | strconv::overflow) => + return invalid; + case u: u8 => + yield u; }; append(buf, u); }; diff --git a/encoding/utf8/decode.ha b/encoding/utf8/decode.ha @@ -9,8 +9,10 @@ export type decoder = struct { // Initializes a new UTF-8 decoder. export fn decode(src: (str | []u8)) decoder = match (src) { - s: str => decoder { src = toutf8(s), ... }, - b: []u8 => decoder { src = b, ... }, +case s: str => + yield decoder { src = toutf8(s), ... }; +case b: []u8 => + yield decoder { src = b, ... }; }; // Returned when more data is needed, i.e. when an incomplete UTF-8 sequence is @@ -93,16 +95,20 @@ export fn prev(d: *decoder) (rune | void | more | invalid) = { let decoder = decode(input); for (let i = 0z; i < len(expected); i += 1) { match (next(&decoder)) { - (invalid | more | void) => abort(), - r: rune => assert(r == expected[i]), + case (invalid | more | void) => + abort(); + case r: rune => + assert(r == expected[i]); }; }; assert(next(&decoder) is void); assert(decoder.offs == len(decoder.src)); for (let i = 0z; i < len(expected); i += 1) { match (prev(&decoder)) { - (invalid | more | void) => abort(), - r: rune => assert(r == expected[len(expected) - i - 1]), + case (invalid | more | void) => + abort(); + case r: rune => + assert(r == expected[len(expected) - i - 1]); }; }; assert(prev(&decoder) is void); @@ -128,10 +134,13 @@ export fn valid(src: (str | []u8)) bool = { let decoder = decode(src); for (true) { match (next(&decoder)) { - void => return true, - invalid => return false, - more => return false, - rune => void, + case void => + return true; + case invalid => + return false; + case more => + return false; + case rune => void; }; }; abort(); diff --git a/errors/rt.ha b/errors/rt.ha @@ -4,16 +4,25 @@ use rt; // interface which is mainly provided to support internal stdlib requirements. export fn errno(errno: rt::errno) error = { switch (errno) { - rt::ECONNREFUSED => return refused, - rt::ECANCELED => return cancelled, - rt::EOVERFLOW => return overflow, - rt::EACCES => return noaccess, - rt::EINVAL => return invalid, - rt::EEXIST => return exists, - rt::ENOENT => return noentry, - rt::ETIME => return timeout, - rt::EBUSY => return busy, - * => void, + case rt::ECONNREFUSED => + return refused; + case rt::ECANCELED => + return cancelled; + case rt::EOVERFLOW => + return overflow; + case rt::EACCES => + return noaccess; + case rt::EINVAL => + return invalid; + case rt::EEXIST => + return exists; + case rt::ENOENT => + return noentry; + case rt::ETIME => + return timeout; + case rt::EBUSY => + return busy; + case => void; }; static assert(size(rt::errno) <= size(opaque_data)); diff --git a/errors/string.ha b/errors/string.ha @@ -9,16 +9,28 @@ // strerror function which provides more context-appropriate error messages for // each of those types. export fn strerror(err: error) const str = match (err) { - busy => "The requested resource is not available", - exists => "An attempt was made to create a resource which already exists", - invalid => "A function was called with an invalid combination of arguments", - noaccess => "The user does not have permission to use this resource", - noentry => "An entry was requested which does not exist", - overflow => "The requested operation caused a numeric overflow condition", - unsupported => "The requested operation is not supported", - timeout => "The requested operation timed out", - cancelled => "The requested operation was cancelled", - refused => "A connection attempt was refused", - nomem => "Unable to allocate sufficient memory for the requested operation", - op: opaque => op.strerror(&op.data), +case busy => + yield "The requested resource is not available"; +case exists => + yield "An attempt was made to create a resource which already exists"; +case invalid => + yield "A function was called with an invalid combination of arguments"; +case noaccess => + yield "The user does not have permission to use this resource"; +case noentry => + yield "An entry was requested which does not exist"; +case overflow => + yield "The requested operation caused a numeric overflow condition"; +case unsupported => + yield "The requested operation is not supported"; +case timeout => + yield "The requested operation timed out"; +case cancelled => + yield "The requested operation was cancelled"; +case refused => + yield "A connection attempt was refused"; +case nomem => + yield "Unable to allocate sufficient memory for the requested operation"; +case op: opaque => + yield op.strerror(&op.data); }; diff --git a/fmt/fmt.ha b/fmt/fmt.ha @@ -172,14 +172,18 @@ export fn fprintf( let iter = strings::iter(fmt); for (true) { let r: rune = match (strings::next(&iter)) { - void => break, - r: rune => r, + case void => + break; + case r: rune => + yield r; }; if (r == '{') { r = match (strings::next(&iter)) { - void => abort("Invalid format string (unterminated '{')"), - r: rune => r, + case void => + abort("Invalid format string (unterminated '{')"); + case r: rune => + yield r; }; let arg = if (r == '{') { @@ -195,37 +199,48 @@ export fn fprintf( }; const arg = match (arg) { - arg: formattable => arg, - * => abort("Invalid formattable"), + case arg: formattable => + yield arg; + case => + abort("Invalid formattable"); }; r = match (strings::next(&iter)) { - void => abort("Invalid format string (unterminated '{')"), - r: rune => r, + case void => + abort("Invalid format string (unterminated '{')"); + case r: rune => + yield r; }; let mod = &modifiers { ... }; let pi: paramindex = void; switch (r) { - ':' => scan_inline_modifiers(&iter, mod), - '%' => scan_parametric_modifiers(&iter, &pi), - '}' => void, - * => abort("Invalid format string"), + case ':' => + scan_inline_modifiers(&iter, mod); + case '%' => + scan_parametric_modifiers(&iter, &pi); + case '}' => void; + case => + abort("Invalid format string"); }; match (pi) { - pi: uint => match (args[pi]) { - pmod: *modifiers => mod = pmod, - * => abort("Explicit parameter is not *fmt::modifier"), - }, - nextparam => { - i += 1; - match (args[i - 1]) { - pmod: *modifiers => mod = pmod, - * => abort("Implicit parameter is not *fmt::modifier"), - }; - }, - void => void, + case pi: uint => + match (args[pi]) { + case pmod: *modifiers => + mod = pmod; + case => + abort("Explicit parameter is not *fmt::modifier"); + }; + case nextparam => + i += 1; + match (args[i - 1]) { + case pmod: *modifiers => + mod = pmod; + case => + abort("Implicit parameter is not *fmt::modifier"); + }; + case void => void; }; if (mod.base == 0) { @@ -235,8 +250,10 @@ export fn fprintf( n += format(s, arg, mod)?; } else if (r == '}') { match (strings::next(&iter)) { - void => abort("Invalid format string (hanging '}')"), - r: rune => assert(r == '}', "Invalid format string (hanging '}')"), + case void => + abort("Invalid format string (hanging '}')"); + case r: rune => + assert(r == '}', "Invalid format string (hanging '}')"); }; n += io::write(s, utf8::encoderune('}'))?; @@ -254,8 +271,10 @@ fn format(out: *io::stream, arg: formattable, mod: *modifiers) (size | io::error let pad: []u8 = []; if (z < mod.width: size) { pad = utf8::encoderune(switch (mod.padding) { - padding::ZEROES => '0', - * => ' ', + case padding::ZEROES => + yield '0'; + case => + yield ' '; }); }; @@ -278,39 +297,46 @@ fn format_raw( out: *io::stream, arg: formattable, mod: *modifiers, -) (size | io::error) = match (arg) { - s: str => io::write(out, strings::toutf8(s)), - r: rune => io::write(out, utf8::encoderune(r)), - b: bool => io::write(out, strings::toutf8(if (b) "true" else "false")), - n: types::numeric => { - let s = strconv::numerictosb(n, mod.base); - yield io::write(out, strings::toutf8(s)); - }, - p: uintptr => { - let s = strconv::uptrtosb(p, mod.base); - yield io::write(out, strings::toutf8(s)); - }, - v: nullable *void => match (v) { - v: *void => { - let s = strconv::uptrtosb(v: uintptr, - strconv::base::HEX_LOWER); +) (size | io::error) = { + match (arg) { + case s: str => + return io::write(out, strings::toutf8(s)); + case r: rune => + return io::write(out, utf8::encoderune(r)); + case b: bool => + return io::write(out, + strings::toutf8(if (b) "true" else "false")); + case n: types::numeric => + const s = strconv::numerictosb(n, mod.base); + return io::write(out, strings::toutf8(s)); + case p: uintptr => + const s = strconv::uptrtosb(p, mod.base); + return io::write(out, strings::toutf8(s)); + case v: nullable *void => + match (v) { + case v: *void => let n = io::write(out, strings::toutf8("0x"))?; + const s = strconv::uptrtosb(v: uintptr, + strconv::base::HEX_LOWER); n += io::write(out, strings::toutf8(s))?; - yield n; - }, - null => format(out, "(null)", mod), - }, - void => io::write(out, strings::toutf8("void")), + return n; + case null => + return format(out, "(null)", mod); + }; + case void => + return io::write(out, strings::toutf8("void")); + }; }; - fn scan_uint(iter: *strings::iterator) uint = { let num: []u8 = []; defer free(num); for (true) { let r = match (strings::next(iter)) { - void => abort("Invalid format string (unterminated '{')"), - r: rune => r, + case void => + abort("Invalid format string (unterminated '{')"); + case r: rune => + yield r; }; if (ascii::isdigit(r)) { @@ -318,9 +344,10 @@ fn scan_uint(iter: *strings::iterator) uint = { } else { strings::push(iter, r); match (strconv::stou(strings::fromutf8(num))) { - (strconv::invalid | strconv::overflow) => - abort("Invalid format string (invalid index)"), - u: uint => return u, + case (strconv::invalid | strconv::overflow) => + abort("Invalid format string (invalid index)"); + case u: uint => + return u; }; }; }; @@ -332,19 +359,24 @@ fn scan_modifier_flags(iter: *strings::iterator, mod: *modifiers) void = { for (true) { let r = match (strings::next(iter)) { - void => abort("Invalid format string (unterminated '{')"), - r: rune => r, + case void => + abort("Invalid format string (unterminated '{')"); + case r: rune => + yield r; }; switch (r) { - '0' => flags |= modflags::ZERO, - '-' => flags |= modflags::MINUS, - ' ' => flags |= modflags::SPACE, - '+' => flags |= modflags::PLUS, - * => { - strings::push(iter, r); - break; - }, + case '0' => + flags |= modflags::ZERO; + case '-' => + flags |= modflags::MINUS; + case ' ' => + flags |= modflags::SPACE; + case '+' => + flags |= modflags::PLUS; + case => + strings::push(iter, r); + break; }; }; @@ -365,8 +397,10 @@ fn scan_modifier_flags(iter: *strings::iterator, mod: *modifiers) void = { fn scan_modifier_width(iter: *strings::iterator, mod: *modifiers) void = { let r = match (strings::next(iter)) { - void => abort("Invalid format string (unterminated '{')"), - r: rune => r, + case void => + abort("Invalid format string (unterminated '{')"); + case r: rune => + yield r; }; let is_digit = ascii::isdigit(r); @@ -379,8 +413,10 @@ fn scan_modifier_width(iter: *strings::iterator, mod: *modifiers) void = { fn scan_modifier_precision(iter: *strings::iterator, mod: *modifiers) void = { let r = match (strings::next(iter)) { - void => abort("Invalid format string (unterminated '{')"), - r: rune => r, + case void => + abort("Invalid format string (unterminated '{')"); + case r: rune => + yield r; }; if (r == '.') { @@ -392,16 +428,23 @@ fn scan_modifier_precision(iter: *strings::iterator, mod: *modifiers) void = { fn scan_modifier_base(iter: *strings::iterator, mod: *modifiers) void = { let r = match (strings::next(iter)) { - void => abort("Invalid format string (unterminated '{')"), - r: rune => r, + case void => + abort("Invalid format string (unterminated '{')"); + case r: rune => + yield r; }; switch (r) { - 'x' => mod.base = strconv::base::HEX_LOWER, - 'X' => mod.base = strconv::base::HEX_UPPER, - 'o' => mod.base = strconv::base::OCT, - 'b' => mod.base = strconv::base::BIN, - * => strings::push(iter, r), + case 'x' => + mod.base = strconv::base::HEX_LOWER; + case 'X' => + mod.base = strconv::base::HEX_UPPER; + case 'o' => + mod.base = strconv::base::OCT; + case 'b' => + mod.base = strconv::base::BIN; + case => + strings::push(iter, r); }; }; @@ -413,16 +456,20 @@ fn scan_inline_modifiers(iter: *strings::iterator, mod: *modifiers) void = { // eat '}' let terminated = match (strings::next(iter)) { - void => false, - r: rune => r == '}', + case void => + yield false; + case r: rune => + yield r == '}'; }; assert(terminated, "Invalid format string (unterminated '{')"); }; fn scan_parameter_index(iter: *strings::iterator, pi: *paramindex) void = { let r = match (strings::next(iter)) { - void => abort("Invalid format string (unterminated '{')"), - r: rune => r, + case void => + abort("Invalid format string (unterminated '{')"); + case r: rune => + yield r; }; let is_digit = ascii::isdigit(r); @@ -439,8 +486,10 @@ fn scan_parametric_modifiers(iter: *strings::iterator, pi: *paramindex) void = { // eat '}' let terminated = match (strings::next(iter)) { - void => false, - r: rune => r == '}', + case void => + yield false; + case r: rune => + yield r == '}'; }; assert(terminated, "Invalid format string (unterminated '{')"); }; diff --git a/fnmatch/fnmatch.ha b/fnmatch/fnmatch.ha @@ -45,14 +45,18 @@ export fn fnmatch(pattern: str, string: str, flag: flags...) bool = { fl |= flag[i]; }; if (fl & flags::PATHNAME != 0) { - return match (fnmatch_pathname(pattern, string, fl)) { - b: bool => b, - * => false, + match (fnmatch_pathname(pattern, string, fl)) { + case b: bool => + return b; + case => + return false; }; } else { - return match (fnmatch_internal(pattern, string, fl)) { - b: bool => b, - * => false, + match (fnmatch_internal(pattern, string, fl)) { + case b: bool => + return b; + case => + return false; }; }; }; @@ -70,14 +74,19 @@ fn fnmatch_pathname( for (true) :outer { start = p_iter; for (true) match (pat_next(&p_iter, fl)?) { - end => break :outer, - r: rune => if (r == '/') break, - bracket => match_bracket(&p_iter, '\0')?, - (question | star) => void, + case end => + break :outer; + case r: rune => + if (r == '/') break; + case bracket => + match_bracket(&p_iter, '\0')?; + case (question | star) => void; }; let s = match (strings::next_token(&tok)) { - void => return false, - s: str => s, + case void => + return false; + case s: str => + yield s; }; strings::prev(&p_iter); let p = cut_tail(strings::iter_str(&start), &p_iter); @@ -87,8 +96,10 @@ fn fnmatch_pathname( }; }; let s = match(strings::next_token(&tok)) { - void => return false, - s: str => s, + case void => + return false; + case s: str => + yield s; }; let p = strings::iter_str(&start); return fnmatch_internal(p, s, fl)? && strings::next_token(&tok) is void; @@ -119,11 +130,16 @@ export fn fnmatch_internal( let copy = s; let rn = strings::next(&copy); let t = match (pat_next(&p, fl)?) { - star => break, - end => return rn is void, - question => rn is rune, - bracket => rn is rune && match_bracket(&p, rn: rune)?, - r: rune => rn is rune && rn: rune == r, + case star => + break; + case end => + return rn is void; + case question => + yield rn is rune; + case bracket => + yield rn is rune && match_bracket(&p, rn: rune)?; + case r: rune => + yield rn is rune && rn: rune == r; }; if (!t) { return false; @@ -136,10 +152,13 @@ export fn fnmatch_internal( let cnt = 0z; for (true; cnt += 1) { match (pat_next(&p, fl)?) { - end => break, - star => p_last = (p, cnt + 1), - bracket => match_bracket(&p, '\0')?, - (question | rune) => void, + case end => + break; + case star => + p_last = (p, cnt + 1); + case bracket => + match_bracket(&p, '\0')?; + case (question | rune) => void; }; }; p = p_last.0; @@ -155,11 +174,20 @@ export fn fnmatch_internal( for (true) { let rn = strings::next(&s); let matches = match (pat_next(&p, fl)?) { - end => if (rn is void) break else return false, - question => rn is rune, - bracket => rn is rune && match_bracket(&p, rn: rune)?, - r: rune => rn is rune && rn: rune == r, - star => abort(), + case end => + if (rn is void) { + break; + } else { + return false; + }; + case question => + yield rn is rune; + case bracket => + yield rn is rune && match_bracket(&p, rn: rune)?; + case r: rune => + yield rn is rune && rn: rune == r; + case star => + abort(); }; if (!matches) { return false; @@ -179,15 +207,18 @@ export fn fnmatch_internal( let copy = s; let rn = strings::next(&copy); let matched = match (pat_next(&p, fl)?) { - end => abort(), - question => rn is rune, - bracket => rn is rune && match_bracket(&p, rn: rune)?, - r: rune => rn is rune && r == rn: rune, - star => { - p_copy = p; - s_copy = s; - continue :outer; - }, + case end => + abort(); + case question => + yield rn is rune; + case bracket => + yield rn is rune && match_bracket(&p, rn: rune)?; + case r: rune => + yield rn is rune && r == rn: rune; + case star => + p_copy = p; + s_copy = s; + continue :outer; }; if (!matched) { break :inner; @@ -195,8 +226,9 @@ export fn fnmatch_internal( s = copy; }; match (strings::next(&s_copy)) { - void => return false, - rune => void, + case void => + return false; + case rune => void; }; }; abort(); @@ -223,50 +255,48 @@ fn match_bracket( }; for (let r = first; true; r = advance_or_err(it)?) { switch (r) { - ']' => break, - '-' => { - let end = advance_or_err(it)?; - if (end == ']') { - // '-' at the end matches itself - strings::push(it, ']'); - last = '-'; - found ||= (c == '-'); - continue; - }; - if (last is void) { - return errors::invalid; - }; - let l = last: rune; - found ||= (l: u32 <= c: u32 && c: u32 <= end: u32); - last = void; // forbid 'a-f-n' - }, - '[' => { - let next_rune = advance_or_err(it)?; - switch (next_rune) { // TODO localization - '=', '.' => return errors::unsupported, - ':' => { - let t = match_ctype(it, c)?; - found ||= t; - }, - * => { - strings::push(it, next_rune); - found ||= (c == '['); - }, - }; - last = '['; - }, - * => { - found ||= (c == r); - last = r; - }, + case ']' => + break; + case '-' => + let end = advance_or_err(it)?; + if (end == ']') { + // '-' at the end matches itself + strings::push(it, ']'); + last = '-'; + found ||= (c == '-'); + continue; + }; + if (last is void) { + return errors::invalid; + }; + let l = last: rune; + found ||= (l: u32 <= c: u32 && c: u32 <= end: u32); + last = void; // forbid 'a-f-n' + case '[' => + let next_rune = advance_or_err(it)?; + switch (next_rune) { // TODO localization + case '=', '.' => + return errors::unsupported; + case ':' => + let t = match_ctype(it, c)?; + found ||= t; + case => + strings::push(it, next_rune); + found ||= (c == '['); + }; + last = '['; + case => + found ||= (c == r); + last = r; }; }; let cnt = len(strings::iter_str(&old)) - len(strings::iter_str(it)); if (last is rune && first == last: rune && cnt >= 4) { switch (first) { - '=', '.', ':' => return errors::invalid, - * => void, + case '=', '.', ':' => + return errors::invalid; + case => void; }; }; return found ^^ inv; @@ -282,12 +312,14 @@ fn match_ctype(it: *strings::iterator, c: rune) (bool | errors::invalid) = { }; }; if (advance_or_err(it)? != ']') { - return errors::invalid; + return errors::invalid; }; let name = strings::sub(s, 0, i - 1); - return match (ctype_name_to_func(name)) { - null => return errors::invalid, - f: *fn(c: rune) bool => f(c), + match (ctype_name_to_func(name)) { + case null => + return errors::invalid; + case f: *fn(c: rune) bool => + return f(c); }; }; @@ -306,32 +338,43 @@ fn ctype_name_to_func(name: str) nullable *fn(c: rune) bool = { ("punct", &ascii::ispunct), ("space", &ascii::isspace), ("upper", &ascii::isupper), ("xdigit",&ascii::isxdigit), ]; - return match (sort::search(map, size(funcmap), &name, &cmp)) { - null => null: nullable *fn(c: rune) bool, - p: *void => (p: *funcmap).1, + match (sort::search(map, size(funcmap), &name, &cmp)) { + case null => + return null: nullable *fn(c: rune) bool; + case p: *void => + return (p: *funcmap).1; }; }; fn pat_next(pat: *strings::iterator, fl: flags) (token | errors::invalid) = { let r = match (strings::next(pat)) { - void => return end, - r: rune => r, + case void => + return end; + case r: rune => + yield r; }; - return switch (r) { - '*' => star, - '?' => question, - '[' => bracket, - // TODO: remove ? (harec bug workaround) - '\\' => if (fl & flags::NOESCAPE == 0) advance_or_err(pat)? - else '\\': token, // TODO: remove cast (harec bug workaround) - * => r, + switch (r) { + case '*' => + return star; + case '?' => + return question; + case '[' => + return bracket; + case '\\' => + // TODO: remove ? (harec bug workaround) + return if (fl & flags::NOESCAPE == 0) advance_or_err(pat)? + else '\\': token; // TODO: remove cast (harec bug workaround) + case => + return r; }; }; fn advance_or_err(it: *strings::iterator) (rune | errors::invalid) = { - return match (strings::next(it)) { - r: rune => r, - void => errors::invalid, + match (strings::next(it)) { + case r: rune => + return r; + case void => + return errors::invalid; }; }; diff --git a/format/xml/+test.ha b/format/xml/+test.ha @@ -87,27 +87,26 @@ fn xmltest(input: str, expected: []token, error: bool) void = { let parser = parse(in) as *parser; for (let i = 0z; i < len(expected); i += 1) { let tok = match (scan(parser)) { - tok: token => tok, - void => abort("Expected token, got void"), - syntaxerr => abort("Expected token, got syntax error"), + case tok: token => + yield tok; + case void => + abort("Expected token, got void"); + case syntaxerr => + abort("Expected token, got syntax error"); }; match (tok) { - el: elementstart => { - let ex = expected[i] as elementstart; - assert(el == ex); - }, - at: attribute => { - let ex = expected[i] as attribute; - assert(at.0 == ex.0 && at.1 == ex.1); - }, - tx: text => { - let ex = expected[i] as text; - assert(tx == ex); - }, - el: elementend => { - let ex = expected[i] as elementend; - assert(el == ex); - }, + case el: elementstart => + let ex = expected[i] as elementstart; + assert(el == ex); + case at: attribute => + let ex = expected[i] as attribute; + assert(at.0 == ex.0 && at.1 == ex.1); + case tx: text => + let ex = expected[i] as text; + assert(tx == ex); + case el: elementend => + let ex = expected[i] as elementend; + assert(el == ex); }; }; if (error) { diff --git a/format/xml/parser.ha b/format/xml/parser.ha @@ -57,57 +57,61 @@ export fn parser_free(par: *parser) void = { // extend their lifetime. export fn scan(par: *parser) (token | void | error) = { switch (par.state) { - state::ROOT, state::ATTRS => want(par, OPTWS)?, - * => void, + case state::ROOT, state::ATTRS => want(par, OPTWS)?; + case => void; }; let rn: rune = match (bufio::scanrune(par.in)?) { - io::EOF => if (par.state == state::ROOT) { + case io::EOF => + if (par.state == state::ROOT) { return syntaxerr; - } else return void, - rn: rune => rn, - }; - return switch (par.state) { - state::ROOT, state::ELEMENT => switch (rn) { - '<' => { - const next = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => { - bufio::unreadrune(par.in, rn); - yield rn; - }, - }; - bufio::unreadrune(par.in, rn); - switch (next) { - '!' => return scan_comment(par), - '?' => return scan_pi(par), - * => void, - }; - let el = scan_element(par)?; - par.state = state::ATTRS; - yield el; - }, - * => { - if (par.state == state::ROOT) { - return syntaxerr; - }; + } else { + return; + }; + case rn: rune => + yield rn; + }; + switch (par.state) { + case state::ROOT, state::ELEMENT => + switch (rn) { + case '<' => + const next = match (bufio::scanrune(par.in)?) { + case io::EOF => + return syntaxerr; + case rn: rune => bufio::unreadrune(par.in, rn); - yield scan_content(par)?; - }, - }, - state::ATTRS => { - if (rn == '/') { - want(par, '>')?; - par.state = state::ELEMENT; - return poptag(par, "")?: elementend; - } else if (rn == '>') { - par.state = state::ELEMENT; - return scan(par)?; - } else if (!isnamestart(rn)) { + yield rn; + }; + bufio::unreadrune(par.in, rn); + switch (next) { + case '!' => + return scan_comment(par); + case '?' => + return scan_pi(par); + case => void; + }; + let el = scan_element(par)?; + par.state = state::ATTRS; + return el; + case => + if (par.state == state::ROOT) { return syntaxerr; }; bufio::unreadrune(par.in, rn); - yield scan_attr(par)?; - }, + return scan_content(par)?; + }; + case state::ATTRS => + if (rn == '/') { + want(par, '>')?; + par.state = state::ELEMENT; + return poptag(par, "")?: elementend; + } else if (rn == '>') { + par.state = state::ELEMENT; + return scan(par)?; + } else if (!isnamestart(rn)) { + return syntaxerr; + }; + bufio::unreadrune(par.in, rn); + return scan_attr(par)?; }; }; @@ -132,19 +136,20 @@ fn scan_attr(par: *parser) (token | error) = { let quot = quote(par)?; strio::reset(par.textbuf); for (true) match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => { - rn = switch (rn) { - '<' => return syntaxerr, - '&' => { - bufio::unreadrune(par.in, rn); - yield scan_entity(par)?; - }, - * => rn, - }; - if (rn == quot) break; - strio::appendrune(par.textbuf, rn)?; - }, + case io::EOF => + return syntaxerr; + case rn: rune => + rn = switch (rn) { + case '<' => + return syntaxerr; + case '&' => + bufio::unreadrune(par.in, rn); + yield scan_entity(par)?; + case => + yield rn; + }; + if (rn == quot) break; + strio::appendrune(par.textbuf, rn)?; }; return (name, strio::string(par.textbuf)): attribute; }; @@ -152,35 +157,42 @@ fn scan_attr(par: *parser) (token | error) = { fn scan_comment(par: *parser) (token | void | error) = { want(par, "<!")?; match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => switch (rn) { - '-' => { // Comments - want(par, '-')?; - }, - '[' => { - want(par, "CDATA[")?; - if (par.state != state::ELEMENT) { - return syntaxerr; - }; - return scan_cdata(par)?; - }, - * => return syntaxerr, - }, + case io::EOF => + return syntaxerr; + case rn: rune => + switch (rn) { + case '-' => // Comments + want(par, '-')?; + case '[' => + want(par, "CDATA[")?; + if (par.state != state::ELEMENT) { + return syntaxerr; + }; + return scan_cdata(par)?; + case => + return syntaxerr; + }; }; for (true) { - let rn = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, + const rn = match (bufio::scanrune(par.in)?) { + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; }; if (rn != '-') continue; - let rn = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, + const rn = match (bufio::scanrune(par.in)?) { + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; }; if (rn != '-') continue; - let rn = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, + const rn = match (bufio::scanrune(par.in)?) { + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; }; if (rn == '>') break; }; @@ -190,25 +202,31 @@ fn scan_comment(par: *parser) (token | void | error) = { fn scan_cdata(par: *parser) (text | error) = { strio::reset(par.textbuf); for (true) { - let rn = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, + const rn = match (bufio::scanrune(par.in)?) { + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; }; if (rn != ']') { strio::appendrune(par.textbuf, rn)!; continue; }; - let rn = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, + const rn = match (bufio::scanrune(par.in)?) { + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; }; if (rn != ']') { strio::appendrune(par.textbuf, rn)!; continue; }; - let rn = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, + const rn = match (bufio::scanrune(par.in)?) { + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; }; if (rn == '>') break; strio::appendrune(par.textbuf, rn)!; @@ -219,21 +237,20 @@ fn scan_cdata(par: *parser) (text | error) = { fn scan_content(par: *parser) (text | error) = { strio::reset(par.textbuf); for (true) match (bufio::scanrune(par.in)?) { - io::EOF => break, - rn: rune => { - rn = switch (rn) { - '<' => { - bufio::unreadrune(par.in, rn); - break; - }, - '&', '%' => { - bufio::unreadrune(par.in, rn); - yield scan_entity(par)?; - }, - * => rn, - }; - strio::appendrune(par.textbuf, rn)?; - }, + case io::EOF => + break; + case rn: rune => + rn = switch (rn) { + case '<' => + bufio::unreadrune(par.in, rn); + break; + case '&', '%' => + bufio::unreadrune(par.in, rn); + yield scan_entity(par)?; + case => + yield rn; + }; + strio::appendrune(par.textbuf, rn)?; }; return strio::string(par.textbuf); }; @@ -242,11 +259,15 @@ fn scan_element(par: *parser) (token | error) = { want(par, '<')?; let close = false; match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => switch (rn) { - '/' => close = true, - * => bufio::unreadrune(par.in, rn), - }, + case io::EOF => + return syntaxerr; + case rn: rune => + switch (rn) { + case '/' => + close = true; + case => + bufio::unreadrune(par.in, rn); + }; }; let name = scan_name(par, par.namebuf)?; if (close) { @@ -261,33 +282,42 @@ fn scan_element(par: *parser) (token | error) = { fn scan_entity(par: *parser) (rune | error) = { want(par, '&')?; let rn = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, - }; - return switch (rn) { - '#' => scan_charref(par), - '%' => syntaxerr, // XXX: Deliberate omission: PEReference - * => { - bufio::unreadrune(par.in, rn); - yield scan_namedent(par); - }, + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; + }; + switch (rn) { + case '#' => + return scan_charref(par); + case '%' => + return syntaxerr; // XXX: Deliberate omission: PEReference + case => + bufio::unreadrune(par.in, rn); + return scan_namedent(par); }; }; fn scan_charref(par: *parser) (rune | error) = { let base = strconv::base::DEC; match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => if (rn == 'x') { + case io::EOF => + return syntaxerr; + case rn: rune => + if (rn == 'x') { base = strconv::base::HEX; - } else bufio::unreadrune(par.in, rn), + } else { + bufio::unreadrune(par.in, rn); + }; }; strio::reset(par.entbuf); for (true) { let rn = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; }; if (ascii::isdigit(rn)) { strio::appendrune(par.entbuf, rn)?; @@ -300,16 +330,18 @@ fn scan_charref(par: *parser) (rune | error) = { if (len(strio::string(par.entbuf)) == 0) { return syntaxerr; }; - return match (strconv::stou32b(strio::string(par.entbuf), base)) { - u: u32 => u: rune, - (strconv::invalid | strconv::overflow) => syntaxerr, + match (strconv::stou32b(strio::string(par.entbuf), base)) { + case u: u32 => + return u: rune; + case (strconv::invalid | strconv::overflow) => + return syntaxerr; }; }; fn scan_namedent(par: *parser) (rune | error) = { - let name = scan_name(par, par.entbuf)?; + const name = scan_name(par, par.entbuf)?; want(par, ';')?; - let map = [ + const map = [ ("lt", '<'), ("gt", '>'), ("amp", '&'), @@ -330,8 +362,10 @@ fn scan_name(par: *parser, buf: *io::stream) (str | error) = { strio::reset(buf); const rn = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; }; if (!isnamestart(rn)) { return syntaxerr; @@ -339,13 +373,15 @@ fn scan_name(par: *parser, buf: *io::stream) (str | error) = { strio::appendrune(buf, rn)!; for (true) match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => if (isname(rn)) { + case io::EOF => + return syntaxerr; + case rn: rune => + if (isname(rn)) { strio::appendrune(buf, rn)!; } else { bufio::unreadrune(par.in, rn); break; - }, + }; }; return strio::string(buf); @@ -365,21 +401,23 @@ fn prolog(par: *parser) (void | error) = { let quot = quote(par)?; want(par, OPTWS, "1.")?; for (true) match (bufio::scanrune(par.in)?) { - io::EOF => break, - rn: rune => if (!ascii::isdigit(rn)) { + case io::EOF => + break; + case rn: rune => + if (!ascii::isdigit(rn)) { bufio::unreadrune(par.in, rn); break; - }, + }; }; want(par, quot)?; let hadws = want(par, OPTWS)?; let encoding = match (bufio::scanrune(par.in)) { - io::EOF => false, - rn: rune => { - bufio::unreadrune(par.in, rn); - yield hadws && rn == 'e'; - }, + case io::EOF => + yield false; + case rn: rune => + bufio::unreadrune(par.in, rn); + yield hadws && rn == 'e'; }; if (encoding) { let attr = scan_attr(par)? as attribute; @@ -388,18 +426,22 @@ fn prolog(par: *parser) (void | error) = { }; // XXX: Deliberate omission: all values other than utf-8 match (ascii::strcasecmp(attr.1, "utf-8")) { - void => return utf8::invalid, - n: int => if (n != 0) return utf8::invalid, + case void => + return utf8::invalid; + case n: int => + if (n != 0) { + return utf8::invalid; + }; }; }; let hadws = want(par, OPTWS)?; let standalone = match (bufio::scanrune(par.in)) { - io::EOF => false, - rn: rune => { - bufio::unreadrune(par.in, rn); - yield hadws && rn == 's'; - }, + case io::EOF => + yield false; + case rn: rune => + bufio::unreadrune(par.in, rn); + yield hadws && rn == 's'; }; if (standalone) { let attr = scan_attr(par)? as attribute; @@ -408,8 +450,12 @@ fn prolog(par: *parser) (void | error) = { }; // XXX: Deliberate omission: non-standalone documents match (ascii::strcasecmp(attr.1, "yes")) { - void => return syntaxerr, - n: int => if (n != 0) return syntaxerr, + case void => + return syntaxerr; + case n: int => + if (n != 0) { + return syntaxerr; + }; }; }; @@ -424,48 +470,55 @@ def WS: whitespace = true; def OPTWS: whitespace = false; fn quote(par: *parser) (rune | error) = { - return match (bufio::scanrune(par.in)?) { - * => return syntaxerr, - rn: rune => switch (rn) { - '"', '\'' => rn, - * => return syntaxerr, - }, + match (bufio::scanrune(par.in)?) { + case rn: rune => + switch (rn) { + case '"', '\'' => + return rn; + case => + return syntaxerr; + }; + case => + return syntaxerr; }; }; fn want(par: *parser, tok: (rune | str | whitespace)...) (bool | error) = { let hadws = false; for (let i = 0z; i < len(tok); i += 1) match (tok[i]) { - x: rune => { - let have = match (bufio::scanrune(par.in)?) { - io::EOF => return syntaxerr, - rn: rune => rn, - }; - if (have != x) { - return syntaxerr; - }; - }, - x: str => { - let iter = strings::iter(x); - for (true) match (strings::next(&iter)) { - rn: rune => want(par, rn)?, - void => break, - }; - }, - ws: whitespace => { - let n = 0; - for (true; n += 1) match (bufio::scanrune(par.in)?) { - io::EOF => break, - rn: rune => if (!ascii::isspace(rn)) { - bufio::unreadrune(par.in, rn); - break; - }, - }; - if (ws && n < 1) { - return syntaxerr; + case x: rune => + let have = match (bufio::scanrune(par.in)?) { + case io::EOF => + return syntaxerr; + case rn: rune => + yield rn; + }; + if (have != x) { + return syntaxerr; + }; + case x: str => + let iter = strings::iter(x); + for (true) match (strings::next(&iter)) { + case rn: rune => + want(par, rn)?; + case void => + break; + }; + case ws: whitespace => + let n = 0; + for (true; n += 1) match (bufio::scanrune(par.in)?) { + case io::EOF => + break; + case rn: rune => + if (!ascii::isspace(rn)) { + bufio::unreadrune(par.in, rn); + break; }; - hadws = n >= 1; - }, + }; + if (ws && n < 1) { + return syntaxerr; + }; + hadws = n >= 1; }; return hadws; }; diff --git a/format/xml/types.ha b/format/xml/types.ha @@ -43,10 +43,11 @@ export type syntaxerr = !void; // TODO: Add line number? export type error = !(syntaxerr | utf8::invalid | io::error); // Converts an [[error]] to a user-friendly string representation. -export fn strerror(err: error) const str = { - return match (err) { - syntaxerr => "Syntax error", - utf8::invalid => "Document is not valid UTF-8", - err: io::error => io::strerror(err), - }; +export fn strerror(err: error) const str = match (err) { +case syntaxerr => + yield "Syntax error"; +case utf8::invalid => + yield "Document is not valid UTF-8"; +case err: io::error => + yield io::strerror(err); }; diff --git a/fs/fs.ha b/fs/fs.ha @@ -5,17 +5,20 @@ use path; // Closes a filesystem. The fs cannot be used after this function is called. export fn close(fs: *fs) void = { match (fs.close) { - null => void, - f: *closefunc => f(fs), + case null => void; + case f: *closefunc => + f(fs); }; }; // Opens a file. If no flags are provided, the default read/write mode is // RDONLY. export fn open(fs: *fs, path: str, flags: flags...) (*io::stream | error) = { - return match (fs.open) { - null => errors::unsupported, - f: *openfunc => f(fs, path, flags...), + match (fs.open) { + case null => + return errors::unsupported; + case f: *openfunc => + return f(fs, path, flags...); }; }; @@ -25,9 +28,11 @@ export fn open(fs: *fs, path: str, flags: flags...) (*io::stream | error) = { // // If no flags are provided, the default read/write mode is RDONLY. export fn open_file(fs: *fs, path: str, flags: flags...) (io::file | error) = { - return match (fs.openfile) { - null => errors::unsupported, - f: *openfilefunc => f(fs, path, flags...), + match (fs.openfile) { + case null => + return errors::unsupported; + case f: *openfilefunc => + return f(fs, path, flags...); }; }; @@ -39,9 +44,11 @@ export fn create( mode: mode, flags: flags... ) (*io::stream | error) = { - return match (fs.create) { - null => errors::unsupported, - f: *createfunc => f(fs, path, mode, flags...), + match (fs.create) { + case null => + return errors::unsupported; + case f: *createfunc => + return f(fs, path, mode, flags...); }; }; @@ -57,17 +64,21 @@ export fn create_file( mode: mode, flags: flags... ) (io::file | error) = { - return match (fs.createfile) { - null => errors::unsupported, - f: *createfilefunc => f(fs, path, mode, flags...), + match (fs.createfile) { + case null => + return errors::unsupported; + case f: *createfilefunc => + return f(fs, path, mode, flags...); }; }; // Removes a file. export fn remove(fs: *fs, path: str) (void | error) = { - return match (fs.remove) { - null => errors::unsupported, - f: *removefunc => f(fs, path), + match (fs.remove) { + case null => + return errors::unsupported; + case f: *removefunc => + return f(fs, path); }; }; @@ -75,9 +86,11 @@ export fn remove(fs: *fs, path: str) (void | error) = { // are both on the same filesystem. See [[move]] for an implementation which // falls back on a "copy & remove" procedure in this situation. export fn rename(fs: *fs, oldpath: str, newpath: str) (void | error) = { - return match (fs.rename) { - null => errors::unsupported, - f: *renamefunc => f(fs, oldpath, newpath), + match (fs.rename) { + case null => + return errors::unsupported; + case f: *renamefunc => + return f(fs, oldpath, newpath); }; }; @@ -85,10 +98,11 @@ export fn rename(fs: *fs, oldpath: str, newpath: str) (void | error) = { // copy and remove if necessary. export fn move(fs: *fs, oldpath: str, newpath: str) (void | error) = { match (rename(fs, oldpath, newpath)) { - cannotrename => void, // Fallback - errors::unsupported => void, // Fallback - void => return, // Success - err: error => return err, + case (cannotrename | errors::unsupported) => void; // Fallback + case err: error => + return err; + case void => + return; // Success }; // TODO: // - If an error occurs, remove the new file. @@ -107,18 +121,22 @@ export fn move(fs: *fs, oldpath: str, newpath: str) (void | error) = { // Pass empty string to yield from the root. The order in which entries are // returned is undefined. export fn iter(fs: *fs, path: str) (*iterator | error) = { - return match (fs.iter) { - null => errors::unsupported, - f: *iterfunc => f(fs, path), + match (fs.iter) { + case null => + return errors::unsupported; + case f: *iterfunc => + return f(fs, path); }; }; // Obtains information about a file or directory. If the target is a symlink, // information is returned about the link, not its target. export fn stat(fs: *fs, path: str) (filestat | error) = { - return match (fs.stat) { - null => errors::unsupported, - f: *statfunc => f(fs, path), + match (fs.stat) { + case null => + return errors::unsupported; + case f: *statfunc => + return f(fs, path); }; }; @@ -126,17 +144,21 @@ export fn stat(fs: *fs, path: str) (filestat | error) = { // separately from the parent filesystem, and its lifetime can outlive that of // its parent. export fn subdir(fs: *fs, path: str) (*fs | error) = { - return match (fs.subdir) { - null => errors::unsupported, - f: *subdirfunc => f(fs, path), + match (fs.subdir) { + case null => + return errors::unsupported; + case f: *subdirfunc => + return f(fs, path); }; }; // Creates a directory. export fn mkdir(fs: *fs, path: str) (void | error) = { - return match (fs.mkdir) { - null => errors::unsupported, - f: *mkdirfunc => f(fs, path), + match (fs.mkdir) { + case null => + return errors::unsupported; + case f: *mkdirfunc => + return f(fs, path); }; }; @@ -145,15 +167,17 @@ export fn mkdirs(fs: *fs, path: str) (void | error) = { let parent = path::dirname(path); if (path != parent) { match (mkdirs(fs, parent)) { - errors::exists => void, - err: error => return err, - void => void, + case errors::exists => void; + case void => void; + case err: error => + return err; }; }; - return match (mkdir(fs, path)) { - errors::exists => void, - err: error => err, - void => void, + match (mkdir(fs, path)) { + case errors::exists => void; + case void => void; + case err: error => + return err; }; }; @@ -163,28 +187,33 @@ export fn rmdir(fs: *fs, path: str) (void | error) = { if (path == "") { return errors::invalid; }; - return match (fs.rmdir) { - null => errors::unsupported, - f: *rmdirfunc => f(fs, path), + match (fs.rmdir) { + case null => + return errors::unsupported; + case f: *rmdirfunc => + return f(fs, path); }; }; // Removes a directory, and anything in it. export fn rmdirall(fs: *fs, path: str) (void | error) = { let it = iter(fs, path)?; - for (true) match (next(it)) { - ent: dirent => { + for (true) { + match (next(it)) { + case ent: dirent => if (ent.name == "." || ent.name == "..") { continue; }; let p = path::join(path, ent.name); defer free(p); + switch (ent.ftype & mode::DIR) { - mode::DIR => rmdirall(fs, p)?, - * => remove(fs, p)?, + case mode::DIR => rmdirall(fs, p)?; + case => remove(fs, p)?; }; - }, - void => break, + case void => + break; + }; }; if (path != "") { return rmdir(fs, path); @@ -194,28 +223,32 @@ export fn rmdirall(fs: *fs, path: str) (void | error) = { // Creates a directory and returns a subdir for it. Some filesystems support // doing this operation atomically, but if not, a fallback is used. export fn mksubdir(fs: *fs, path: str) (*fs | error) = { - return match (fs.mksubdir) { - null => { - mkdir(fs, path)?; - yield subdir(fs, path); - }, - f: *mksubdirfunc => f(fs, path), + match (fs.mksubdir) { + case null => + mkdir(fs, path)?; + return subdir(fs, path); + case f: *mksubdirfunc => + return f(fs, path); }; }; // Changes mode flags on a file or directory. export fn chmod(fs: *fs, path: str, mode: mode) (void | error) = { - return match (fs.chmod) { - f: *chmodfunc => f(fs, path, mode), - null => errors::unsupported, + match (fs.chmod) { + case null => + return errors::unsupported; + case f: *chmodfunc => + return f(fs, path, mode); }; }; // Changes ownership of a file. export fn chown(fs: *fs, path: str, uid: uint, gid: uint) (void | error) = { - return match (fs.chown) { - f: *chownfunc => f(fs, path, uid, gid), - null => errors::unsupported, + match (fs.chown) { + case null => + return errors::unsupported; + case f: *chownfunc => + return f(fs, path, uid, gid); }; }; @@ -224,8 +257,9 @@ export fn chown(fs: *fs, path: str, uid: uint, gid: uint) (void | error) = { // the return value. export fn resolve(fs: *fs, path: str) str = { match (fs.resolve) { - f: *resolvefunc => return f(fs, path), - null => void, + case null => void; + case f: *resolvefunc => + return f(fs, path); }; abort(); // TODO }; diff --git a/fs/mem/+test.ha b/fs/mem/+test.ha @@ -57,8 +57,10 @@ use strconv; defer free(it); let count = 0z; for (true) match (it.next(it)) { - void => break, - d: fs::dirent => count += 1, + case void => + break; + case d: fs::dirent => + count += 1; }; assert(count == 6); @@ -136,8 +138,10 @@ use strconv; defer free(it); let count = 0z; for (true) match (it.next(it)) { - void => break, - d: fs::dirent => count += 1, + case void => + break; + case d: fs::dirent => + count += 1; }; assert(count == limit); diff --git a/fs/mem/mem.ha b/fs/mem/mem.ha @@ -77,10 +77,14 @@ fn file_flags(flags: fs::flags...) ((io::mode, bool) | fs::error) = { }; let appnd = fl & fs::flags::APPEND == fs::flags::APPEND; let mode = switch (fl & (~fs::flags::APPEND)) { - fs::flags::RDONLY => io::mode::READ, - fs::flags::WRONLY => io::mode::WRITE, - fs::flags::RDWR => io::mode::RDWR, - * => abort("invalid flag combination"), + case fs::flags::RDONLY => + yield io::mode::READ; + case fs::flags::WRONLY => + yield io::mode::WRITE; + case fs::flags::RDWR => + yield io::mode::RDWR; + case => + abort("invalid flag combination"); }; return (mode, appnd); }; @@ -96,8 +100,9 @@ fn create( let parent = fs: *inode; match (inode_find(parent, path)) { - errors::noentry => void, - * => return errors::exists, + case errors::noentry => void; + case => + return errors::exists; }; if (path::dirname(path) != path) { parent = inode_find(parent, path::dirname(path))?; @@ -129,13 +134,15 @@ fn open( }; fn stat(fs: *fs::fs, path: str) (fs::filestat | fs::error) = { - return match(inode_find(fs: *inode, path)?.data) { - directory => fs::filestat { mode = fs::mode::DIR | 0o777, ... }, - f: file => fs::filestat { + match (inode_find(fs: *inode, path)?.data) { + case directory => + return fs::filestat { mode = fs::mode::DIR | 0o777, ... }; + case f: file => + return fs::filestat { mode = fs::mode::REG | 0o777, mask = fs::stat_mask::SIZE, sz = len(f), - }, + }; }; }; @@ -148,8 +155,9 @@ fn mkdir(fs: *fs::fs, path: str) (void | fs::error) = { fn mksubdir(fs: *fs::fs, path: str) (*fs::fs | fs::error) = { let parent = fs: *inode; match (inode_find(parent, path)) { - errors::noentry => void, - * => return errors::exists, + case errors::noentry => void; + case => + return errors::exists; }; if (path::dirname(path) != path) { parent = inode_find(parent, path::dirname(path))?; @@ -190,15 +198,18 @@ fn iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = { }): *fs::iterator; }; -fn next(iter: *fs::iterator) (fs::dirent | void) = match (_next(iter)) { - null => void, - ino: *inode => fs::dirent { - name = ino.name, - ftype = match (ino.data) { - directory => fs::mode::DIR, - file => fs::mode::REG, - }, - }, +fn next(iter: *fs::iterator) (fs::dirent | void) = { + match (_next(iter)) { + case null => void; + case ino: *inode => + return fs::dirent { + name = ino.name, + ftype = match (ino.data) { + case directory => yield fs::mode::DIR; + case file => yield fs::mode::REG; + }, + }; + }; }; fn _next(it: *fs::iterator) nullable *inode = { @@ -211,11 +222,11 @@ fn _next(it: *fs::iterator) nullable *inode = { let p = iter.parent.data as directory; iter.idx += 1; for (iter.idx < len(p.ents)) match (p.ents[iter.idx]) { - null => iter.idx += 1, - ino: *inode => { - iter.curr = ino.next; - return ino; - }, + case null => + iter.idx += 1; + case ino: *inode => + iter.curr = ino.next; + return ino; }; return null; }; @@ -260,15 +271,18 @@ fn close_rec(ino: *inode) void = { }; let it = iterator { it = fs_iter, parent = ino, ... }; for (true) match (_next(&it: *fs::iterator)) { - null => break, - ino: *inode => { - ino.parent = null; - match (ino.data) { - file => inode_free(ino), - directory => close_rec(ino), - * => abort("unreachable"), - }; - }, + case null => + break; + case ino: *inode => + ino.parent = null; + match (ino.data) { + case file => + inode_free(ino); + case directory => + close_rec(ino); + case => + abort("unreachable"); + }; }; inode_free(ino); }; diff --git a/fs/mem/util.ha b/fs/mem/util.ha @@ -32,11 +32,11 @@ fn ensure(parent: *inode) void = { parent.data = dir; for (let i = 0z; i < len(old); i += 1) { for (true) match (old[i]) { - null => break, - ino: *inode => { - old[i] = ino.next; - _inode_insert(parent, ino); - }, + case null => + break; + case ino: *inode => + old[i] = ino.next; + _inode_insert(parent, ino); }; }; }; @@ -62,15 +62,15 @@ fn unlink(parent: *inode, ino: *inode) void = { let prev = &p.ents[ino.hash % len(p.ents): u64]; let it = *prev; for (true) match (it) { - null => break, - ii: *inode => { - if (ii.hash == ino.hash && ii.name == ino.name) { - *prev = ii.next; - break; - }; - prev = &ii.next; - it = ii.next; - }, + case null => + break; + case ii: *inode => + if (ii.hash == ino.hash && ii.name == ino.name) { + *prev = ii.next; + break; + }; + prev = &ii.next; + it = ii.next; }; p.sz -= 1; parent.data = p; @@ -79,8 +79,10 @@ fn unlink(parent: *inode, ino: *inode) void = { fn inode_free(ino: *inode) void = { match (ino.data) { - d: directory => free(d.ents), - f: file => free(f), + case d: directory => + free(d.ents); + case f: file => + free(f); }; free(ino.name); free(ino); @@ -98,16 +100,18 @@ fn find_rec(dir: *inode, name: str, it: *path::iterator) (*inode | fs::error) = let p = dir.data as directory; let bucket = p.ents[hash_of(name) % len(p.ents): u64]; for (true) match (bucket) { - null => break, - ino: *inode => { - if (name == ino.name) { - return match (path::next(it)) { - void => ino, - name: str => find_rec(ino, name, it), - }; + case null => + break; + case ino: *inode => + if (name == ino.name) { + match (path::next(it)) { + case void => + return ino; + case name: str => + return find_rec(ino, name, it); }; - bucket = ino.next; - }, + }; + bucket = ino.next; }; return errors::noentry; }; diff --git a/fs/util.ha b/fs/util.ha @@ -5,13 +5,20 @@ use strings; // Returns a human-friendly representation of an error. export fn strerror(err: error) const str = match (err) { - wrongtype => "Wrong entry type for requested operation", - cannotrename => "Unable to perform rename operation (try move instead)", - errors::noentry => "File or directory not found", - errors::noaccess => "Permission denied", - errors::exists => "File or directory exists", - errors::invalid => "Invalid argument", - err: io::error => io::strerror(err), +case wrongtype => + yield "Wrong entry type for requested operation"; +case cannotrename => + yield "Unable to perform rename operation (try move instead)"; +case errors::noentry => + yield "File or directory not found"; +case errors::noaccess => + yield "Permission denied"; +case errors::exists => + yield "File or directory exists"; +case errors::invalid => + yield "Invalid argument"; +case err: io::error => + yield io::strerror(err); }; // Converts a mode into a Unix-like mode string (e.g. "-rw-r--r--"). The string @@ -88,8 +95,10 @@ export fn readdir(fs: *fs, path: str) ([]dirent | error) = { let ents: []dirent = []; for (true) { match (next(i)) { - d: dirent => append(ents, dirent_dup(&d)), - void => break, + case d: dirent => + append(ents, dirent_dup(&d)); + case void => + break; }; }; return ents; diff --git a/getopt/getopts.ha b/getopt/getopts.ha @@ -126,13 +126,18 @@ export fn parse(args: []str, help: help...) command = { const r = next as rune; for (let j = 0z; j < len(help); j += 1) :help { let p: parameter_help = match (help[j]) { - cmd_help => continue :help, - f: flag_help => if (r == f.0) { + case cmd_help => + continue :help; + case f: flag_help => + if (r == f.0) { append(opts, (r, "")); continue :flag; - } else continue :help, - p: parameter_help => if (r == p.0) p - else continue :help, + } else { + continue :help; + }; + case p: parameter_help => + yield if (r == p.0) p else + continue :help; }; if (len(d.src) == d.offs) { if (i + 1 >= len(args)) { @@ -156,13 +161,13 @@ export fn parse(args: []str, help: help...) command = { os::exit(1); }; match (next) { - rune => abort(), // Unreachable - void => void, - (utf8::more | utf8::invalid) => { - errmsg(args[0], "invalid UTF-8 in arguments", - void, help); - os::exit(1); - }, + case void => void; + case rune => + abort(); // Unreachable + case (utf8::more | utf8::invalid) => + errmsg(args[0], "invalid UTF-8 in arguments", void, + help); + os::exit(1); }; }; return command { @@ -229,29 +234,28 @@ export fn printhelp(s: *io::stream, name: str, help: []help) void = { printusage(s, name, help); for (let i = 0z; i < len(help); i += 1) match (help[i]) { - cmd_help => void, - (flag_help | parameter_help) => { - // Only print this if there are flags to show - fmt::fprint(s, "\n")!; - break; - }, + case cmd_help => void; + case (flag_help | parameter_help) => + // Only print this if there are flags to show + fmt::fprint(s, "\n")!; + break; }; for (let i = 0z; i < len(help); i += 1) match (help[i]) { - cmd_help => void, - f: flag_help => { - fmt::fprintfln(s, "-{}: {}", f.0: rune, f.1)!; - }, - p: parameter_help => { - fmt::fprintfln(s, "-{} <{}>: {}", p.0: rune, p.1, p.2)!; - }, + case cmd_help => void; + case f: flag_help => + fmt::fprintfln(s, "-{}: {}", f.0: rune, f.1)!; + case p: parameter_help => + fmt::fprintfln(s, "-{} <{}>: {}", p.0: rune, p.1, p.2)!; }; }; fn errmsg(name: str, err: str, opt: (rune | void), help: []help) void = { fmt::errorfln("{}: {}{}", name, err, match (opt) { - r: rune => r, - void => "", + case r: rune => + yield r; + case void => + yield ""; })!; printusage(os::stderr, name, help); }; diff --git a/hare/ast/decl.ha b/hare/ast/decl.ha @@ -60,34 +60,30 @@ export type decl = struct { // Frees resources associated with a declaration. export fn decl_free(d: decl) void = match (d.decl) { - g: []decl_global => { - for (let i = 0z; i < len(g); i += 1) { - free(g[i].symbol); - ident_free(g[i].ident); - type_free(g[i]._type); - expr_free(g[i].init); - }; - free(g); - }, - t: []decl_type => { - for (let i = 0z; i < len(t); i += 1) { - ident_free(t[i].ident); - type_free(t[i]._type); - }; - free(t); - }, - f: decl_func => { - free(f.symbol); - ident_free(f.ident); - type_free(f.prototype); - if (f.body is expr) expr_free(f.body as expr); - }, - c: []decl_const => { - for (let i = 0z; i < len(c); i += 1) { - ident_free(c[i].ident); - type_free(c[i]._type); - expr_free(c[i].init); - }; - free(c); - }, +case g: []decl_global => + for (let i = 0z; i < len(g); i += 1) { + free(g[i].symbol); + ident_free(g[i].ident); + type_free(g[i]._type); + expr_free(g[i].init); + }; + free(g); +case t: []decl_type => + for (let i = 0z; i < len(t); i += 1) { + ident_free(t[i].ident); + type_free(t[i]._type); + }; + free(t); +case f: decl_func => + free(f.symbol); + ident_free(f.ident); + type_free(f.prototype); + if (f.body is expr) expr_free(f.body as expr); +case c: []decl_const => + for (let i = 0z; i < len(c); i += 1) { + ident_free(c[i].ident); + type_free(c[i]._type); + expr_free(c[i].init); + }; + free(c); }; diff --git a/hare/ast/expr.ha b/hare/ast/expr.ha @@ -276,7 +276,7 @@ export type len_expr = *expr; export type match_case = struct { name: str, _type: *_type, - value: *expr, + exprs: []*expr, }; // A match expression. @@ -285,11 +285,11 @@ export type match_case = struct { export type match_expr = struct { value: *expr, cases: []match_case, - default: nullable *expr, + default: []*expr, }; // An offset expression. -// +// // offset(foo.bar) export type offset_expr = void; // TODO @@ -324,8 +324,8 @@ export type slice_expr = struct { // // value => expr export type switch_case = struct { - options: []*expr, // [] for * - value: *expr, + options: []*expr, // [] for default case + exprs: []*expr, }; // A switch expression. @@ -378,177 +378,185 @@ export type expr = struct { // Frees resources associated with a Hare [[expr]]ession. export fn expr_free(e: (expr | nullable *expr)) void = match (e) { - e: nullable *expr => match (e) { - null => void, - e: *expr => { - expr_free(*e); - free(e); - }, - }, - e: expr => match (e.expr) { - a: access_expr => match (a) { - i: access_identifier => ident_free(i), - i: access_index => { - expr_free(i.object); - expr_free(i.index); - }, - f: access_field => { - expr_free(f.object); - free(f.field); - }, - t: access_tuple => { - expr_free(t.object); - expr_free(t.value); - }, - }, - a: alloc_expr => { - expr_free(a.init); - expr_free(a.capacity); - }, - a: append_expr => { - expr_free(a.object); - match (a.variadic) { - null => void, - v: *expr => expr_free(v), - }; +case e: nullable *expr => + match (e) { + case null => void; + case e: *expr => + expr_free(*e); + free(e); + }; +case e: expr => + match (e.expr) { + case a: access_expr => + match (a) { + case i: access_identifier => + ident_free(i); + case i: access_index => + expr_free(i.object); + expr_free(i.index); + case f: access_field => + expr_free(f.object); + free(f.field); + case t: access_tuple => + expr_free(t.object); + expr_free(t.value); + }; + case a: alloc_expr => + expr_free(a.init); + expr_free(a.capacity); + case a: append_expr => + expr_free(a.object); + match (a.variadic) { + case null => void; + case v: *expr => + expr_free(v); + }; + for (let i = 0z; i < len(a.values); i += 1) { + expr_free(a.values[i]); + }; + free(a.values); + case a: assert_expr => + expr_free(a.cond); + expr_free(a.message); + case a: assign_expr => + expr_free(a.object); + expr_free(a.value); + case b: binarithm_expr => + expr_free(b.lvalue); + expr_free(b.rvalue); + case b: binding_expr => + for (let i = 0z; i < len(b.bindings); i += 1) { + free(b.bindings[i].name); + type_free(b.bindings[i]._type); + expr_free(b.bindings[i].init); + }; + free(b.bindings); + case b: break_expr => + free(b); + case c: call_expr => + expr_free(c.lvalue); + for (let i = 0z; i < len(c.args); i += 1) { + expr_free(c.args[i]); + }; + free(c.args); + case c: cast_expr => + expr_free(c.value); + type_free(c._type); + case c: compound_expr => + for (let i = 0z; i < len(c.exprs); i += 1) { + expr_free(c.exprs[i]); + }; + free(c.exprs); + free(c.label); + case c: constant_expr => + match (c) { + case (void | _null | ...lex::value) => void; + case a: array_constant => for (let i = 0z; i < len(a.values); i += 1) { expr_free(a.values[i]); }; free(a.values); - }, - a: assert_expr => { - expr_free(a.cond); - expr_free(a.message); - }, - a: assign_expr => { - expr_free(a.object); - expr_free(a.value); - }, - b: binarithm_expr => { - expr_free(b.lvalue); - expr_free(b.rvalue); - }, - b: binding_expr => { - for (let i = 0z; i < len(b.bindings); i += 1) { - free(b.bindings[i].name); - type_free(b.bindings[i]._type); - expr_free(b.bindings[i].init); - }; - free(b.bindings); - }, - b: break_expr => free(b), - c: call_expr => { - expr_free(c.lvalue); - for (let i = 0z; i < len(c.args); i += 1) { - expr_free(c.args[i]); - }; - free(c.args); - }, - c: cast_expr => { - expr_free(c.value); - type_free(c._type); - }, - c: compound_expr => { - for (let i = 0z; i < len(c.exprs); i += 1) { - expr_free(c.exprs[i]); + case s: struct_constant => + struct_constant_free(s); + case t: tuple_constant => + for (let i = 0z; i < len(t); i += 1) { + expr_free(t[i]); }; - free(c.exprs); - free(c.label); - }, - c: constant_expr => match(c) { - (void | _null | ...lex::value) => void, - a: array_constant => { - for (let i = 0z; i < len(a.values); i += 1) { - expr_free(a.values[i]); - }; - free(a.values); - }, - s: struct_constant => struct_constant_free(s), - t: tuple_constant => { - for (let i = 0z; i < len(t); i += 1) { - expr_free(t[i]); - }; - free(t); - }, - }, - c: continue_expr => free(c), - d: defer_expr => expr_free(d: *expr), - d: delete_expr => expr_free(d.object), - f: for_expr => { - expr_free(f.bindings); - expr_free(f.cond); - expr_free(f.afterthought); - expr_free(f.body); - }, - f: free_expr => expr_free(f: *expr), - i: if_expr => { - expr_free(i.cond); - expr_free(i.tbranch); - expr_free(i.fbranch); - }, - e: insert_expr => { - expr_free(e.object); - match (e.variadic) { - null => void, - v: *expr => expr_free(v), - }; - for (let i = 0z; i < len(e.values); i += 1) { - expr_free(e.values[i]); + free(t); + }; + case c: continue_expr => + free(c); + case d: defer_expr => + expr_free(d: *expr); + case d: delete_expr => + expr_free(d.object); + case f: for_expr => + expr_free(f.bindings); + expr_free(f.cond); + expr_free(f.afterthought); + expr_free(f.body); + case f: free_expr => + expr_free(f: *expr); + case i: if_expr => + expr_free(i.cond); + expr_free(i.tbranch); + expr_free(i.fbranch); + case e: insert_expr => + expr_free(e.object); + match (e.variadic) { + case null => void; + case v: *expr => + expr_free(v); + }; + for (let i = 0z; i < len(e.values); i += 1) { + expr_free(e.values[i]); + }; + free(e.values); + case l: len_expr => + expr_free(l: *expr); + case m: match_expr => + expr_free(m.value); + for (let i = 0z; i < len(m.cases); i += 1) { + free(m.cases[i].name); + type_free(m.cases[i]._type); + const exprs = m.cases[i].exprs; + for (let i = 0z; i < len(exprs); i += 1) { + expr_free(exprs[i]); }; - free(e.values); - }, - l: len_expr => expr_free(l: *expr), - m: match_expr => { - expr_free(m.value); - for (let i = 0z; i < len(m.cases); i += 1) { - free(m.cases[i].name); - type_free(m.cases[i]._type); - expr_free(m.cases[i].value); + free(exprs); + }; + free(m.cases); + for (let i = 0z; i < len(m.default); i += 1) { + expr_free(m.default[i]); + }; + free(m.default); + case offset_expr => + abort(); // TODO + case p: propagate_expr => + expr_free(p.expr); + case r: return_expr => + expr_free(r: *expr); + case s: size_expr => + type_free(s: *_type); + case s: slice_expr => + expr_free(s.object); + expr_free(s.start); + expr_free(s.end); + case s: switch_expr => + expr_free(s.value); + for (let i = 0z; i < len(s.cases); i += 1) { + let opts = s.cases[i].options; + for (let j = 0z; j < len(opts); j += 1) { + expr_free(opts[j]); }; - free(m.cases); - }, - offset_expr => abort(), // TODO - p: propagate_expr => expr_free(p.expr), - r: return_expr => expr_free(r: *expr), - s: size_expr => type_free(s: *_type), - s: slice_expr => { - expr_free(s.object); - expr_free(s.start); - expr_free(s.end); - }, - s: switch_expr => { - expr_free(s.value); - for (let i = 0z; i < len(s.cases); i += 1) { - let opts = s.cases[i].options; - for (let j = 0z; j < len(opts); j += 1) { - expr_free(opts[j]); - }; - free(opts); - expr_free(s.cases[i].value); + free(opts); + + let exprs = s.cases[i].exprs; + for (let j = 0z; j < len(exprs); j += 1) { + expr_free(exprs[j]); }; - free(s.cases); - }, - u: unarithm_expr => expr_free(u.operand), - y: yield_expr => { - free(y.label); - expr_free(y.value); - }, - }, + free(exprs); + }; + free(s.cases); + case u: unarithm_expr => + expr_free(u.operand); + case y: yield_expr => + free(y.label); + expr_free(y.value); + }; }; fn struct_constant_free(s: struct_constant) void = { ident_free(s.alias); for (let i = 0z; i < len(s.fields); i += 1) { match (s.fields[i]) { - v: struct_value => { - free(v.name); - type_free(v._type); - expr_free(v.init); - }, - c: *struct_constant => { - struct_constant_free(*c); - free(c); - }, + case v: struct_value => + free(v.name); + type_free(v._type); + expr_free(v.init); + case c: *struct_constant => + struct_constant_free(*c); + free(c); }; }; free(s.fields); diff --git a/hare/ast/import.ha b/hare/ast/import.ha @@ -19,17 +19,16 @@ export type import = (import_module | import_alias | import_objects); // Frees resources associated with an [[import]]. export fn import_free(import: import) void = { match (import) { - m: import_module => ident_free(m: ident), - a: import_alias => { - ident_free(a.ident); - free(a.alias); - }, - o: import_objects => { - ident_free(o.ident); - for (let i = 0z; i < len(o.objects); i += 1) { - free(o.objects[i]); - }; - free(o.objects); - }, + case m: import_module => + ident_free(m: ident); + case a: import_alias => + ident_free(a.ident); + free(a.alias); + case o: import_objects => + ident_free(o.ident); + for (let i = 0z; i < len(o.objects); i += 1) { + free(o.objects[i]); + }; + free(o.objects); }; }; diff --git a/hare/ast/type.ha b/hare/ast/type.ha @@ -125,81 +125,84 @@ export type _type = struct { fn struct_type_free(t: (struct_type | union_type)) void = { let membs = match (t) { - s: struct_type => s: []struct_member, - u: union_type => u: []struct_member, + case s: struct_type => + yield s: []struct_member; + case u: union_type => + yield u: []struct_member; }; for (let i = 0z; i < len(membs); i += 1) { match (membs[i]._offset) { - null => void, - e: *expr => expr_free(e), + case null => void; + case e: *expr => + expr_free(e); }; match (membs[i].member) { - f: struct_field => { - free(f.name); - type_free(f._type); - }, - e: struct_embedded => { - type_free(e: *_type); - }, - a: struct_alias => ident_free(a), + case f: struct_field => + free(f.name); + type_free(f._type); + case e: struct_embedded => + type_free(e: *_type); + case a: struct_alias => + ident_free(a); }; }; free(membs); }; // Frees resources associated with a [[_type]]. -export fn type_free(t: (_type | nullable *_type)) void = match (t) { - t: nullable *_type => match (t) { - null => void, - t: *_type => { +export fn type_free(t: (_type | nullable *_type)) void = { + match (t) { + case t: nullable *_type => + match (t) { + case null => void; + case t: *_type => type_free(*t); free(t); - }, - }, - t: _type => match (t.repr) { - a: alias_type => ident_free(a.ident), - builtin_type => void, - e: enum_type => { + }; + case t: _type => + match (t.repr) { + case a: alias_type => + ident_free(a.ident); + case builtin_type => void; + case e: enum_type => for (let i = 0z; i < len(e.values); i += 1) { free(e.values[i].name); match (e.values[i].value) { - null => void, - v: *expr => expr_free(v), + case null => void; + case v: *expr => + expr_free(v); }; }; free(e.values); - }, - f: func_type => { + case f: func_type => type_free(f.result); for (let i = 0z; i < len(f.params); i += 1) { free(f.params[i].name); type_free(f.params[i]._type); }; free(f.params); - }, - l: list_type => { + case l: list_type => match (l.length) { - e: *expr => { - expr_free(*e); - free(e); - }, - * => void, + case e: *expr => + expr_free(*e); + free(e); + case => void; }; type_free(l.members); - }, - p: pointer_type => type_free(p.referent), - s: (struct_type | union_type) => struct_type_free(s), - t: tagged_type => { + case p: pointer_type => + type_free(p.referent); + case s: (struct_type | union_type) => + struct_type_free(s); + case t: tagged_type => for (let i = 0z; i < len(t); i += 1) { type_free(t[i]); }; free(t); - }, - t: tuple_type => { + case t: tuple_type => for (let i = 0z; i < len(t); i += 1) { type_free(t[i]); }; free(t); - }, - }, + }; + }; }; diff --git a/hare/lex/+test.ha b/hare/lex/+test.ha @@ -38,13 +38,21 @@ use strings; assert(t.2.line == 1234 && t.2.col == 1234); }; -fn vassert(expected: value, actual: value) void = match (expected) { - expected: str => assert(actual as str == expected), - expected: rune => assert(actual as rune == expected), - expected: i64 => assert(actual as i64 == expected), - expected: u64 => assert(actual as u64 == expected), - expected: f64 => assert(actual as f64 == expected), - void => assert(actual is void), +fn vassert(expected: value, actual: value) void = { + match (expected) { + case expected: str => + assert(actual as str == expected); + case expected: rune => + assert(actual as rune == expected); + case expected: i64 => + assert(actual as i64 == expected); + case expected: u64 => + assert(actual as u64 == expected); + case expected: f64 => + assert(actual as f64 == expected); + case void => + assert(actual is void); + }; }; fn lextest(in: str, expected: []token) void = { @@ -54,11 +62,11 @@ fn lextest(in: str, expected: []token) void = { for (let i = 0z; i < len(expected); i += 1) { let etok = expected[i]; let tl = match (lex(&lexer)) { - tl: token => tl, - err: error => { - fmt::errorfln("{}: {}", i, strerror(err))!; - abort(); - }, + case tl: token => + yield tl; + case err: error => + fmt::errorfln("{}: {}", i, strerror(err))!; + abort(); }; assert(tl.0 == etok.0); vassert(tl.1, etok.1); @@ -341,16 +349,16 @@ type op = enum { defer free(t); for (let i = 0z; i < len(ops); i += 1) { switch (ops[i]) { - op::LEX => append(t, lex(&lexer)!), - op::NEXT => append(r, next(&lexer) as (rune, location)), - op::UNGET => { - unget(&lexer, r[len(r) - 1]); - delete(r[len(r) - 1]); - }, - op::UNLEX => { - unlex(&lexer, t[len(t) - 1]); - delete(t[len(t) - 1]); - }, + case op::LEX => + append(t, lex(&lexer)!); + case op::NEXT => + append(r, next(&lexer) as (rune, location)); + case op::UNGET => + unget(&lexer, r[len(r) - 1]); + delete(r[len(r) - 1]); + case op::UNLEX => + unlex(&lexer, t[len(t) - 1]); + delete(t[len(t) - 1]); }; let loc = mkloc(&lexer); let ploc = prevloc(&lexer); diff --git a/hare/lex/lex.ha b/hare/lex/lex.ha @@ -37,10 +37,12 @@ export type error = !(io::error | syntax); // Returns a human-friendly string for a given error export fn strerror(err: error) const str = { static let buf: [2048]u8 = [0...]; - return match (err) { - err: io::error => io::strerror(err), - s: syntax => fmt::bsprintf(buf, "{}:{},{}: Syntax error: {}", - s.0.path, s.0.line, s.0.col, s.1), + match (err) { + case err: io::error => + return io::strerror(err); + case s: syntax => + return fmt::bsprintf(buf, "{}:{},{}: Syntax error: {}", + s.0.path, s.0.line, s.0.col, s.1); }; }; @@ -71,11 +73,10 @@ export fn comment(lex: *lexer) str = lex.comment; // Returns the next token from the lexer. export fn lex(lex: *lexer) (token | error) = { match (lex.un) { - tok: token => { - lex.un = void; - return tok; - }, - void => void, + case tok: token => + lex.un = void; + return tok; + case void => void; }; defer { @@ -84,8 +85,10 @@ export fn lex(lex: *lexer) (token | error) = { }; let r = match (nextw(lex)?) { - io::EOF => return (ltok::EOF, void, mkloc(lex)), - r: (rune, location) => r, + case io::EOF => + return (ltok::EOF, void, mkloc(lex)); + case r: (rune, location) => + yield r; }; if (is_name(r.0, false)) { @@ -98,29 +101,37 @@ export fn lex(lex: *lexer) (token | error) = { }; let tok = switch (r.0) { - * => return syntaxerr(r.1, "invalid character"), - '"', '\'' => { - unget(lex, r); - return lex_rn_str(lex); - }, - '.', '<', '>', '&', '|', '^' => { - unget(lex, r); - return lex3(lex); - }, - '*', '%', '/', '+', '-', ':', '!', '=' => { - unget(lex, r); - return lex2(lex); - }, - '~' => ltok::BNOT, - ',' => ltok::COMMA, - '{' => ltok::LBRACE, - '[' => ltok::LBRACKET, - '(' => ltok::LPAREN, - '}' => ltok::RBRACE, - ']' => ltok::RBRACKET, - ')' => ltok::RPAREN, - ';' => ltok::SEMICOLON, - '?' => ltok::QUESTION, + case '"', '\'' => + unget(lex, r); + return lex_rn_str(lex); + case '.', '<', '>', '&', '|', '^' => + unget(lex, r); + return lex3(lex); + case '*', '%', '/', '+', '-', ':', '!', '=' => + unget(lex, r); + return lex2(lex); + case '~' => + yield ltok::BNOT; + case ',' => + yield ltok::COMMA; + case '{' => + yield ltok::LBRACE; + case '[' => + yield ltok::LBRACKET; + case '(' => + yield ltok::LPAREN; + case '}' => + yield ltok::RBRACE; + case ']' => + yield ltok::RBRACKET; + case ')' => + yield ltok::RPAREN; + case ';' => + yield ltok::SEMICOLON; + case '?' => + yield ltok::QUESTION; + case => + return syntaxerr(r.1, "invalid character"); }; return (tok, void, r.1); }; @@ -130,9 +141,11 @@ fn is_name(r: rune, num: bool) bool = fn ncmp(a: const *void, b: const *void) int = { let a = a: const *str, b = b: const *str; - return match (ascii::strcmp(*a, *b)) { - void => abort("non-ascii name"), // TODO: Bubble me up - i: int => i, + match (ascii::strcmp(*a, *b)) { + case void => + abort("non-ascii name"); // TODO: Bubble me up + case i: int => + return i; }; }; @@ -141,9 +154,11 @@ fn lex_unicode(lex: *lexer, loc: location, n: size) (rune | error) = { let buf: [9]u8 = [0...]; for (let i = 0z; i < n; i += 1z) { let r = match (next(lex)?) { - io::EOF => return syntaxerr(loc, - "unexpected EOF scanning for escape"), - r: (rune, location) => r.0, + case io::EOF => + return syntaxerr(loc, + "unexpected EOF scanning for escape"); + case r: (rune, location) => + yield r.0; }; if (!ascii::isxdigit(r)) { return syntaxerr(loc, @@ -157,60 +172,76 @@ fn lex_unicode(lex: *lexer, loc: location, n: size) (rune | error) = { fn lex_rune(lex: *lexer, loc: location) (rune | error) = { let r = match (next(lex)?) { - io::EOF => return syntaxerr(loc, - "unexpected EOF scanning for rune"), - r: (rune, location) => r.0, + case io::EOF => + return syntaxerr(loc, "unexpected EOF scanning for rune"); + case r: (rune, location) => + yield r.0; }; if (r != '\\') { return r; }; r = match (next(lex)?) { - io::EOF => return syntaxerr(loc, - "unexpected EOF scanning for escape"), - r: (rune, location) => r.0, - }; - return switch (r) { - '\\' => '\\', - '\'' => '\'', - '0' => '\0', - 'a' => '\a', - 'b' => '\b', - 'f' => '\f', - 'n' => '\n', - 'r' => '\r', - 't' => '\t', - 'v' => '\v', - '"' => '\"', - 'x' => lex_unicode(lex, loc, 2), - 'u' => lex_unicode(lex, loc, 4), - 'U' => lex_unicode(lex, loc, 8), + case io::EOF => + return syntaxerr(loc, "unexpected EOF scanning for escape"); + case r: (rune, location) => + yield r.0; + }; + switch (r) { + case '\\' => + return '\\'; + case '\'' => + return '\''; + case '0' => + return '\0'; + case 'a' => + return '\a'; + case 'b' => + return '\b'; + case 'f' => + return '\f'; + case 'n' => + return '\n'; + case 'r' => + return '\r'; + case 't' => + return '\t'; + case 'v' => + return '\v'; + case '"' => + return '\"'; + case 'x' => + return lex_unicode(lex, loc, 2); + case 'u' => + return lex_unicode(lex, loc, 4); + case 'U' => + return lex_unicode(lex, loc, 8); }; }; fn lex_string(lex: *lexer, loc: location) (token | error) = { let buf = strio::dynamic(); for (true) match (next(lex)?) { - io::EOF => return syntaxerr(loc, "unexpected EOF scanning string literal"), - r: (rune, location) => - if (r.0 == '"') break - else { - unget(lex, r); - let r = lex_rune(lex, loc)?; - strio::appendrune(buf, r)?; - }, + case io::EOF => + return syntaxerr(loc, "unexpected EOF scanning string literal"); + case r: (rune, location) => + if (r.0 == '"') break + else { + unget(lex, r); + let r = lex_rune(lex, loc)?; + strio::appendrune(buf, r)?; + }; }; match (nextw(lex)?) { - io::EOF => void, - r: (rune, location) => { - if (r.0 == '"') { - const tok = lex_string(lex, loc)?; - const next = tok.1 as str; - strio::concat(buf, next)!; - free(next); - } else { - unget(lex, r); - }; - }, + case io::EOF => void; + case r: (rune, location) => + if (r.0 == '"') { + const tok = lex_string(lex, loc)?; + const next = tok.1 as str; + strio::concat(buf, next)!; + free(next); + } else { + unget(lex, r); + }; }; return (ltok::LIT_STR, strio::finish(buf), loc); }; @@ -218,22 +249,28 @@ fn lex_string(lex: *lexer, loc: location) (token | error) = { fn lex_rn_str(lex: *lexer) (token | error) = { const loc = mkloc(lex); let r = match (next(lex)) { - r: (rune, location) => r.0, - (io::EOF | io::error) => abort(), + case r: (rune, location) => + yield r.0; + case (io::EOF | io::error) => + abort(); }; switch (r) { - '\"' => return lex_string(lex, loc), - '\'' => void, - * => abort(), // Invariant + case '\'' => void; + case '\"' => + return lex_string(lex, loc); + case => + abort(); // Invariant }; // Rune literal let ret: token = (ltok::LIT_RUNE, lex_rune(lex, loc)?, loc); match (next(lex)?) { - io::EOF => - return syntaxerr(loc, "unexpected EOF"), - n: (rune, location) => if (n.0 != '\'') - return syntaxerr(n.1, "expected \"\'\""), + case io::EOF => + return syntaxerr(loc, "unexpected EOF"); + case n: (rune, location) => + if (n.0 != '\'') { + return syntaxerr(n.1, "expected \"\'\""); + }; }; return ret; }; @@ -241,22 +278,21 @@ fn lex_rn_str(lex: *lexer) (token | error) = { fn lex_name(lex: *lexer, loc: location, label: bool) (token | error) = { let buf = strio::dynamic(); match (next(lex)) { - r: (rune, location) => { - assert(is_name(r.0, false)); - strio::appendrune(buf, r.0)!; - }, - (io::EOF | io::error) => abort(), + case r: (rune, location) => + assert(is_name(r.0, false)); + strio::appendrune(buf, r.0)!; + case (io::EOF | io::error) => + abort(); }; for (true) match (next(lex)?) { - io::EOF => break, - r: (rune, location) => { - if (!is_name(r.0, true)) { - unget(lex, r); - break; - }; - strio::appendrune(buf, r.0)?; - }, + case io::EOF => break; + case r: (rune, location) => + if (!is_name(r.0, true)) { + unget(lex, r); + break; + }; + strio::appendrune(buf, r.0)?; }; let n = strio::finish(buf); @@ -264,23 +300,27 @@ fn lex_name(lex: *lexer, loc: location, label: bool) (token | error) = { return (ltok::LABEL, n, loc); }; - return match (sort::search(bmap[..ltok::LAST_KEYWORD+1], + match (sort::search(bmap[..ltok::LAST_KEYWORD+1], size(str), &n, &ncmp)) { - null => (ltok::NAME, n, loc), - v: *void => { - defer free(n); - let tok = v: uintptr - &bmap[0]: uintptr; - tok /= size(str): uintptr; - yield (tok: ltok, void, loc); - }, + case null => + return (ltok::NAME, n, loc); + case v: *void => + defer free(n); + let tok = v: uintptr - &bmap[0]: uintptr; + tok /= size(str): uintptr; + return (tok: ltok, void, loc); }; }; fn lex_comment(lexr: *lexer) (token | error) = { if (lexr.flags & flags::COMMENTS != flags::COMMENTS) { for (true) match (next(lexr)?) { - io::EOF => break, - r: (rune, location) => if (r.0 == '\n') break, + case io::EOF => + break; + case r: (rune, location) => + if (r.0 == '\n') { + break; + }; }; return lex(lexr); }; @@ -288,11 +328,13 @@ fn lex_comment(lexr: *lexer) (token | error) = { let buf = strio::dynamic(); defer io::close(buf); for (true) match (next(lexr)?) { - io::EOF => break, - r: (rune, location) => { - strio::appendrune(buf, r.0)!; - if (r.0 == '\n') break; - }, + case io::EOF => + break; + case r: (rune, location) => + strio::appendrune(buf, r.0)!; + if (r.0 == '\n') { + break; + }; }; let new = strings::concat(lexr.comment, strio::string(buf)); free(lexr.comment); @@ -304,14 +346,18 @@ fn lex_literal(lex: *lexer) (token | error) = { const loc = mkloc(lex); let chars: []u8 = []; let r = match (next(lex)?) { - io::EOF => return (ltok::EOF, void, loc), - r: (rune, location) => r, + case io::EOF => + return (ltok::EOF, void, loc); + case r: (rune, location) => + yield r; }; if (r.0 == '-') { append(chars, utf8::encoderune(r.0)...); r = match (next(lex)?) { - io::EOF => return (ltok::EOF, void, loc), - r: (rune, location) => r, + case io::EOF => + return (ltok::EOF, void, loc); + case r: (rune, location) => + yield r; }; }; @@ -319,21 +365,31 @@ fn lex_literal(lex: *lexer) (token | error) = { if (r.0 == '0') { append(chars, utf8::encoderune(r.0)...); r = match (next(lex)?) { - io::EOF => return (ltok::LIT_ICONST, 0i64, loc), - r: (rune, location) => r, + case io::EOF => + return (ltok::LIT_ICONST, 0i64, loc); + case r: (rune, location) => + yield r; }; switch (r.0) { - 'b' => base = 2, - 'o' => base = 8, - 'x' => base = 16, - * => unget(lex, r), + case 'b' => + base = 2; + case 'o' => + base = 8; + case 'x' => + base = 16; + case => + unget(lex, r); }; } else unget(lex, r); let basechrs = switch (base) { - 2 => "01", - 8 => "01234567", - 10 => "0123456789", - 16 => "0123456789ABCDEFabcdef", + case 2 => + yield "01"; + case 8 => + yield "01234567"; + case 10 => + yield "0123456789"; + case 16 => + yield "0123456789ABCDEFabcdef"; }; let suff: (size | void) = void; @@ -342,18 +398,23 @@ fn lex_literal(lex: *lexer) (token | error) = { let float = false; for (true) { r = match (next(lex)?) { - io::EOF => break, - r: (rune, location) => r, + case io::EOF => + break; + case r: (rune, location) => + yield r; }; if (!strings::contains(basechrs, r.0)) switch (r.0) { - '.' => if (float || exp is size || suff is size + case '.' => + if (float || exp is size || suff is size || base != 10) { unget(lex, r); break; } else { r = match (next(lex)?) { - io::EOF => break, - r: (rune, location) => r, + case io::EOF => + break; + case r: (rune, location) => + yield r; }; if (!strings::contains(basechrs, r.0)) { unget(lex, r); @@ -367,8 +428,9 @@ fn lex_literal(lex: *lexer) (token | error) = { unget(lex, r); float = true; append(chars, utf8::encoderune('.')...); - }, - 'e', 'E' => if (exp is size || suff is size || base != 10) { + }; + case 'e', 'E' => + if (exp is size || suff is size || base != 10) { unget(lex, r); break; } else { @@ -376,17 +438,21 @@ fn lex_literal(lex: *lexer) (token | error) = { append(chars, utf8::encoderune(r.0)...); exp = len(chars); r = match (next(lex)?) { - io::EOF => break, - r: (rune, location) => r, + case io::EOF => + break; + case r: (rune, location) => + yield r; }; switch (r.0) { - '+', '-' => append(chars, - utf8::encoderune(r.0)...), - * => unget(lex, r), + case '+', '-' => + append(chars, utf8::encoderune(r.0)...); + case => + unget(lex, r); }; basechrs = "0123456789"; - }, - 'i', 'u', 'f', 'z' => if (suff is size) { + }; + case 'i', 'u', 'f', 'z' => + if (suff is size) { unget(lex, r); break; } else { @@ -394,39 +460,46 @@ fn lex_literal(lex: *lexer) (token | error) = { if (end == 0) end = len(chars); append(chars, utf8::encoderune(r.0)...); basechrs = "0123456789"; - }, - * => { - unget(lex, r); - break; - }, + }; + case => + unget(lex, r); + break; } else append(chars, utf8::encoderune(r.0)...); }; if (end == 0) end = len(chars); let exp = match (exp) { - void => "0", - exp: size => { - let end = match (suff) { - void => len(chars), - suff: size => suff, - }; - yield strings::fromutf8(chars[exp..end]); - }, + case void => + yield "0"; + case exp: size => + let end = match (suff) { + case void => + yield len(chars); + case suff: size => + yield suff; + }; + yield strings::fromutf8(chars[exp..end]); }; let exp = match (strconv::stoi(exp)) { - exp: int => exp, - strconv::invalid => abort(), // Shouldn't be lexed in - strconv::overflow => - return syntaxerr(loc, "overflow in exponent"), + case exp: int => + yield exp; + case strconv::invalid => + abort(); // Shouldn't be lexed in + case strconv::overflow => + return syntaxerr(loc, "overflow in exponent"); }; let floatend = match (suff) { - suff: size => suff, - void => len(chars), + case suff: size => + yield suff; + case void => + yield len(chars); }; let suff = match (suff) { - suff: size => strings::fromutf8(chars[suff..]), - void => "", + case suff: size => + yield strings::fromutf8(chars[suff..]); + case void => + yield ""; }; let suff = if (suff == "u8") ltok::LIT_U8 else if (suff == "u16") ltok::LIT_U16 @@ -446,47 +519,53 @@ fn lex_literal(lex: *lexer) (token | error) = { else return syntaxerr(loc, "invalid literal suffix"); let exp = if (exp < 0) switch (suff) { - ltok::LIT_F32, ltok::LIT_F64, ltok::LIT_FCONST => exp: size, - * => return syntaxerr(loc, - "invalid negative exponent of integer"), + case ltok::LIT_F32, ltok::LIT_F64, ltok::LIT_FCONST => + yield exp: size; + case => return syntaxerr(loc, + "invalid negative exponent of integer"); } else exp: size; let val = strings::fromutf8(chars[..end]); let val = switch (suff) { - ltok::LIT_U8, ltok::LIT_U16, ltok::LIT_U32, ltok::LIT_U64, - ltok::LIT_UINT, ltok::LIT_SIZE => strconv::stou64b(val, base), - ltok::LIT_ICONST => match (strconv::stoi64b(val, base)) { - i: i64 => i, - strconv::invalid => abort(), - strconv::overflow => if (chars[0] != '-': u32: u8) { + case ltok::LIT_U8, ltok::LIT_U16, ltok::LIT_U32, ltok::LIT_U64, + ltok::LIT_UINT, ltok::LIT_SIZE => + yield strconv::stou64b(val, base); + case ltok::LIT_ICONST => + yield match (strconv::stoi64b(val, base)) { + case i: i64 => + yield i; + case strconv::invalid => + abort(); + case strconv::overflow => + yield if (chars[0] != '-': u32: u8) { suff = ltok::LIT_U64; yield strconv::stou64b(val, base); - } else strconv::overflow, - }, - ltok::LIT_I8, ltok::LIT_I16, ltok::LIT_I32, ltok::LIT_I64, - ltok::LIT_INT => strconv::stoi64b(val, base), - ltok::LIT_F32, ltok::LIT_F64, ltok::LIT_FCONST => { - val = strings::fromutf8(chars[..floatend]); - yield strconv::stof64(val); - }, + } else strconv::overflow; + }; + case ltok::LIT_I8, ltok::LIT_I16, ltok::LIT_I32, ltok::LIT_I64, + ltok::LIT_INT => + yield strconv::stoi64b(val, base); + case ltok::LIT_F32, ltok::LIT_F64, ltok::LIT_FCONST => + val = strings::fromutf8(chars[..floatend]); + yield strconv::stof64(val); }; let val = match (val) { - val: u64 => { - for (let i = 0z; i < exp; i += 1) { - val *= 10; - }; - yield val; - }, - val: i64 => { - for (let i = 0z; i < exp; i += 1) { - val *= 10; - }; - yield val; - }, - val: f64 => val, - strconv::invalid => abort(), // Shouldn't be lexed in - strconv::overflow => - return syntaxerr(loc, "overflow in exponent"), + case val: u64 => + for (let i = 0z; i < exp; i += 1) { + val *= 10; + }; + yield val; + case val: i64 => + for (let i = 0z; i < exp; i += 1) { + val *= 10; + }; + yield val; + case val: f64 => + yield val; + case strconv::invalid => + abort(); // Shouldn't be lexed in + case strconv::overflow => + return syntaxerr(loc, "overflow in exponent"); }; return (suff, val, loc); @@ -495,60 +574,77 @@ fn lex_literal(lex: *lexer) (token | error) = { fn lex2(lex: *lexer) (token | error) = { let first = next(lex)? as (rune, location); let tok: (ltok, [](rune, ltok)) = switch (first.0) { - '*' => (ltok::TIMES, [('=', ltok::TIMESEQ)]), - '%' => (ltok::MODULO, [('=', ltok::MODEQ)]), - '/' => match (next(lex)?) { - r: (rune, location) => switch (r.0) { - '=' => return (ltok::DIVEQ, void, first.1), - '/' => return lex_comment(lex), - * => { - unget(lex, r); - return (ltok::DIV, void, first.1); - }, - }, - io::EOF => return (ltok::DIV, void, first.1), - }, - '+' => (ltok::PLUS, [('=', ltok::PLUSEQ)]), - '-' => match (next(lex)?) { - r: (rune, location) => switch (r.0) { - '=' => return (ltok::MINUSEQ, void, first.1), - * => if (ascii::isdigit(r.0)) { + case '*' => + yield (ltok::TIMES, [('=', ltok::TIMESEQ)]); + case '%' => + yield (ltok::MODULO, [('=', ltok::MODEQ)]); + case '/' => + match (next(lex)?) { + case r: (rune, location) => + switch (r.0) { + case '=' => + return (ltok::DIVEQ, void, first.1); + case '/' => + return lex_comment(lex); + case => + unget(lex, r); + return (ltok::DIV, void, first.1); + }; + case io::EOF => + return (ltok::DIV, void, first.1); + }; + case '+' => + yield (ltok::PLUS, [('=', ltok::PLUSEQ)]); + case '-' => + match (next(lex)?) { + case r: (rune, location) => + switch (r.0) { + case '=' => + return (ltok::MINUSEQ, void, first.1); + case => + if (ascii::isdigit(r.0)) { unget(lex, r); unget(lex, first); return lex_literal(lex); } else { unget(lex, r); return (ltok::MINUS, void, first.1); - }, - }, - io::EOF => return (ltok::MINUS, void, first.1), - }, - ':' => match (next(lex)?) { - r: (rune, location) => switch (r.0) { - ':' => return (ltok::DOUBLE_COLON, void, first.1), - * => { - unget(lex, r); - return if (is_name(r.0, false)) { - yield lex_name(lex, first.1, true)?; - } else (ltok::COLON, void, first.1); - }, - }, - io::EOF => return (ltok::COLON, void, first.1), - }, - '!' => (ltok::LNOT, [('=', ltok::NEQUAL)]), - '=' => (ltok::EQUAL, [('=', ltok::LEQUAL), ('>', ltok::CASE)]), - * => return syntaxerr(first.1, "unknown token sequence"), + }; + }; + case io::EOF => + return (ltok::MINUS, void, first.1); + }; + case ':' => + match (next(lex)?) { + case r: (rune, location) => + switch (r.0) { + case ':' => + return (ltok::DOUBLE_COLON, void, first.1); + case => + unget(lex, r); + return if (is_name(r.0, false)) { + yield lex_name(lex, first.1, true)?; + } else (ltok::COLON, void, first.1); + }; + case io::EOF => + return (ltok::COLON, void, first.1); + }; + case '!' => + yield (ltok::LNOT, [('=', ltok::NEQUAL)]); + case '=' => + yield (ltok::EQUAL, [('=', ltok::LEQUAL), ('>', ltok::ARROW)]); + case => + return syntaxerr(first.1, "unknown token sequence"); }; match (next(lex)?) { - r: (rune, location) => { - for (let i = 0z; i < len(tok.1); i += 1) { - if (tok.1[i].0 == r.0) { - return (tok.1[i].1, void, first.1); - }; + case r: (rune, location) => + for (let i = 0z; i < len(tok.1); i += 1) { + if (tok.1[i].0 == r.0) { + return (tok.1[i].1, void, first.1); }; - unget(lex, r); - }, - io::EOF => void, + }; + unget(lex, r); + case io::EOF => void; }; return (tok.0, void, first.1); }; @@ -556,29 +652,40 @@ fn lex2(lex: *lexer) (token | error) = { fn lex3(lex: *lexer) (token | error) = { let r = next(lex)? as (rune, location); let toks = switch (r.0) { - '.' => { - let tok = if (try(lex, '.') is void) ltok::DOT - else if (try(lex, '.') is void) ltok::SLICE - else ltok::ELLIPSIS; - return (tok, void, r.1); - }, - '<' => [ltok::LESS, ltok::LESSEQ, ltok::LSHIFT, ltok::LSHIFTEQ], - '>' => [ltok::GREATER, ltok::GREATEREQ, ltok::RSHIFT, - ltok::RSHIFTEQ], - '&' => [ltok::BAND, ltok::BANDEQ, ltok::LAND, ltok::LANDEQ], - '|' => [ltok::BOR, ltok::BOREQ, ltok::LOR, ltok::LOREQ], - '^' => [ltok::BXOR, ltok::BXOREQ, ltok::LXOR, ltok::LXOREQ], - * => return syntaxerr(r.1, "unknown token sequence"), + case '.' => + let tok = if (try(lex, '.') is void) ltok::DOT + else if (try(lex, '.') is void) ltok::SLICE + else ltok::ELLIPSIS; + return (tok, void, r.1); + case '<' => + yield [ltok::LESS, ltok::LESSEQ, ltok::LSHIFT, ltok::LSHIFTEQ]; + case '>' => + yield [ltok::GREATER, ltok::GREATEREQ, ltok::RSHIFT, + ltok::RSHIFTEQ]; + case '&' => + yield [ltok::BAND, ltok::BANDEQ, ltok::LAND, ltok::LANDEQ]; + case '|' => + yield [ltok::BOR, ltok::BOREQ, ltok::LOR, ltok::LOREQ]; + case '^' => + yield [ltok::BXOR, ltok::BXOREQ, ltok::LXOR, ltok::LXOREQ]; + case => + return syntaxerr(r.1, "unknown token sequence"); }; let idx = match (try(lex, r.0, '=')?) { - void => 0, // X - n: (rune, location) => switch (n.0) { - '=' => 1, // X= - * => match (try(lex, '=')?) { - void => 2, // XX - (rune, location) => 3, // XX= - }, - }, + case void => + yield 0; // X + case n: (rune, location) => + yield switch (n.0) { + case '=' => + yield 1; // X= + case => + yield match (try(lex, '=')?) { + case void => + yield 2; // XX + case (rune, location) => + yield 3; // XX= + }; + }; }; return (toks[idx], void, r.1); }; @@ -593,44 +700,47 @@ export fn unlex(lex: *lexer, tok: token) void = { fn next(lex: *lexer) ((rune, location) | io::EOF | io::error) = { match (lex.rb[0]) { - void => void, - r: ((rune, location) | io::EOF) => { - lex.rb[0] = lex.rb[1]; - lex.rb[1] = void; - return r; - }, + case void => void; + case r: ((rune, location) | io::EOF) => + lex.rb[0] = lex.rb[1]; + lex.rb[1] = void; + return r; }; - return match (bufio::scanrune(lex.in)) { - e: (io::EOF | io::error) => e, - r: rune => { - const loc = mkloc(lex); - let tmp = lex.prevrlocs; - lex.prevrlocs[1..] = tmp[..len(tmp) - 1]; - lex.prevrlocs[0] = loc; - lexloc(lex, r); - return (r, loc); - }, + match (bufio::scanrune(lex.in)) { + case e: (io::EOF | io::error) => + return e; + case r: rune => + const loc = mkloc(lex); + let tmp = lex.prevrlocs; + lex.prevrlocs[1..] = tmp[..len(tmp) - 1]; + lex.prevrlocs[0] = loc; + lexloc(lex, r); + return (r, loc); }; }; fn nextw(lex: *lexer) ((rune, location) | io::EOF | io::error) = { for (true) match (next(lex)) { - e: (io::error | io::EOF) => return e, - r: (rune, location) => if (!ascii::isspace(r.0)) { + case e: (io::error | io::EOF) => + return e; + case r: (rune, location) => + if (!ascii::isspace(r.0)) { return r; } else { free(lex.comment); lex.comment = ""; - }, + }; }; abort(); }; fn try(lex: *lexer, want: rune...) ((rune, location) | void | io::error) = { let r = match (next(lex)?) { - io::EOF => return void, - r: (rune, location) => r, + case io::EOF => + return; + case r: (rune, location) => + yield r; }; assert(len(want) > 0); for (let i = 0z; i < len(want); i += 1) { @@ -643,12 +753,13 @@ fn try(lex: *lexer, want: rune...) ((rune, location) | void | io::error) = { fn lexloc(lex: *lexer, r: rune) void = { switch (r) { - '\n' => { - lex.loc.0 += 1; - lex.loc.1 = 1; - }, - '\t' => lex.loc.1 += 8 - lex.loc.1 % 8 + 1, - * => lex.loc.1 += 1, + case '\n' => + lex.loc.0 += 1; + lex.loc.1 = 1; + case '\t' => + lex.loc.1 += 8 - lex.loc.1 % 8 + 1; + case => + lex.loc.1 += 1; }; }; @@ -660,25 +771,33 @@ fn unget(lex: *lexer, r: ((rune, location) | io::EOF)) void = { lex.rb[0] = r; }; -export fn mkloc(lex: *lexer) location = match (lex.un) { - t: token => lex.prevunlocs[1].1, - void => match (lex.rb[0]) { - r: (rune, location) => r.1, - void => location { - path = lex.path, - line = lex.loc.0, - col = lex.loc.1, - }, - }, +export fn mkloc(lex: *lexer) location = { + match (lex.un) { + case t: token => + return lex.prevunlocs[1].1; + case void => + match (lex.rb[0]) { + case r: (rune, location) => + return r.1; + case void => + return location { + path = lex.path, + line = lex.loc.0, + col = lex.loc.1, + }; + }; + }; }; -export fn prevloc(lex: *lexer) location = match (lex.un) { - t: token => lex.prevunlocs[1].0, - void => { +export fn prevloc(lex: *lexer) location = { + match (lex.un) { + case t: token => + return lex.prevunlocs[1].0; + case void => let i = 0z; for (i < len(lex.rb); i += 1) if (lex.rb[i] is void) break; return lex.prevrlocs[i]; - }, + }; }; fn syntaxerr(loc: location, why: str) error = (loc, why); diff --git a/hare/lex/token.ha b/hare/lex/token.ha @@ -19,6 +19,7 @@ export type ltok = enum uint { ASSERT, BOOL, BREAK, + CASE, CHAR, CONST, CONTINUE, @@ -70,6 +71,7 @@ export type ltok = enum uint { LAST_KEYWORD = YIELD, // Operators + ARROW, BAND, BANDEQ, BNOT, @@ -77,7 +79,6 @@ export type ltok = enum uint { BOREQ, BXOR, BXOREQ, - CASE, COLON, COMMA, DIV, @@ -164,6 +165,7 @@ const bmap: [_]str = [ "assert", "bool", "break", + "case", "char", "const", "continue", @@ -212,6 +214,7 @@ const bmap: [_]str = [ "use", "void", "yield", + "=>", "&", "&=", "~", @@ -288,27 +291,48 @@ export fn tokstr(tok: token) const str = { if (tok.0 <= ltok::LAST_BTOK) { return bmap[tok.0: int]; }; - return switch (tok.0) { - ltok::LIT_U8 => "u8", - ltok::LIT_U16 => "u16", - ltok::LIT_U32 => "u32", - ltok::LIT_U64 => "u64", - ltok::LIT_UINT => "uint", - ltok::LIT_SIZE => "size", - ltok::LIT_I8 => "i8", - ltok::LIT_I16 => "i16", - ltok::LIT_I32 => "i32", - ltok::LIT_I64 => "i64", - ltok::LIT_INT => "int", - ltok::LIT_ICONST => "iconst", - ltok::LIT_F32 => "f32", - ltok::LIT_F64 => "f64", - ltok::LIT_FCONST => "fconst", - ltok::LIT_RUNE => "rune", - ltok::LIT_STR => "str", - ltok::NAME => tok.1 as str, - ltok::LABEL => abort(), // TODO - ltok::EOF => "EOF", - * => abort(), + switch (tok.0) { + case ltok::LIT_U8 => + return "u8"; + case ltok::LIT_U16 => + return "u16"; + case ltok::LIT_U32 => + return "u32"; + case ltok::LIT_U64 => + return "u64"; + case ltok::LIT_UINT => + return "uint"; + case ltok::LIT_SIZE => + return "size"; + case ltok::LIT_I8 => + return "i8"; + case ltok::LIT_I16 => + return "i16"; + case ltok::LIT_I32 => + return "i32"; + case ltok::LIT_I64 => + return "i64"; + case ltok::LIT_INT => + return "int"; + case ltok::LIT_ICONST => + return "iconst"; + case ltok::LIT_F32 => + return "f32"; + case ltok::LIT_F64 => + return "f64"; + case ltok::LIT_FCONST => + return "fconst"; + case ltok::LIT_RUNE => + return "rune"; + case ltok::LIT_STR => + return "str"; + case ltok::NAME => + return tok.1 as str; + case ltok::LABEL => + abort(); // TODO + case ltok::EOF => + return "EOF"; + case => + abort(); }; }; diff --git a/hare/module/context.ha b/hare/module/context.ha @@ -32,25 +32,27 @@ export fn context_init(tags: []tag, defs: []str, harepath: str) context = { tags = tags, defines = defs, paths = match (os::getenv("HAREPATH")) { - void => alloc([ + case void => + yield alloc([ strings::dup(harepath), dirs::data("hare"), strings::dup("."), - ]), - s: str => { - let sl = strings::split(s, ":"); - let path: []str = alloc([], len(sl) + 1); - for (let i = 0z; i < len(sl); i += 1) { - append(path, strings::dup(sl[i])); - }; - append(path, strings::dup(".")); - free(sl); - yield path; - }, + ]); + case s: str => + let sl = strings::split(s, ":"); + let path: []str = alloc([], len(sl) + 1); + for (let i = 0z; i < len(sl); i += 1) { + append(path, strings::dup(sl[i])); + }; + append(path, strings::dup(".")); + free(sl); + yield path; }, cache: str = match (os::getenv("HARECACHE")) { - void => dirs::cache("hare"), - s: str => strings::dup(s), + case void => + yield dirs::cache("hare"); + case s: str => + yield strings::dup(s); }, ... }; diff --git a/hare/module/manifest.ha b/hare/module/manifest.ha @@ -53,9 +53,12 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = { defer free(mpath); let file = match (fs::open(ctx.fs, mpath, fs::flags::RDONLY)) { - errors::noentry => return manifest, - err: fs::error => return err, - file: *io::stream => file, + case errors::noentry => + return manifest; + case err: fs::error => + return err; + case file: *io::stream => + yield file; }; defer io::close(file); @@ -65,15 +68,19 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = { let file = bufio::buffered(file, buf, []); for (true) { let line = match (bufio::scanline(file)?) { - io::EOF => break, - line: []u8 => line, + case io::EOF => + break; + case line: []u8 => + yield line; }; defer free(line); let line = match (strings::try_fromutf8(line)) { + case utf8::invalid => // Treat an invalid manifest as empty - utf8::invalid => return manifest, - s: str => s, + return manifest; + case s: str => + yield s; }; if (strings::has_prefix(line, "#")) { @@ -82,49 +89,76 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = { let tok = strings::tokenize(line, " "); let kind = match (strings::next_token(&tok)) { - void => continue, - s: str => s, + case void => + continue; + case s: str => + yield s; }; - if (kind == "version") { + switch (kind) { + case "version" => let ver = match (strings::next_token(&tok)) { - void => return manifest, - s: str => s, + case void => + return manifest; + case s: str => + yield s; }; match (strconv::stoi(ver)) { - v: int => if (v != VERSION) { + case v: int => + if (v != VERSION) { return manifest; - }, - * => return manifest, + }; + case => + return manifest; }; - } else if (kind == "input") { + case "input" => let hash = match (strings::next_token(&tok)) { - void => return manifest, s: str => s, + case void => + return manifest; + case s: str => + yield s; }, path = match (strings::next_token(&tok)) { - void => return manifest, s: str => s, + case void => + return manifest; + case s: str => + yield s; }, inode = match (strings::next_token(&tok)) { - void => return manifest, s: str => s, + case void => + return manifest; + case s: str => + yield s; }, mtime = match (strings::next_token(&tok)) { - void => return manifest, s: str => s, + case void => + return manifest; + case s: str => + yield s; }; let hash = match (hex::decode(hash)) { - * => return manifest, - b: []u8 => b, + case b: []u8 => + yield b; + case => + return manifest; }; let inode = match (strconv::stoz(inode)) { - * => return manifest, - z: size => z, + case z: size => + yield z; + case => + return manifest; }; let mtime = match (strconv::stoi64(mtime)) { - * => return manifest, - i: i64 => time::from_unix(i), + case i: i64 => + yield time::from_unix(i); + case => + return manifest; }; let parsed = parse_name(path); let ftype = match (type_for_ext(path)) { - void => return manifest, - ft: filetype => ft, + case void => + return manifest; + case ft: filetype => + yield ft; }; append(inputs, input { @@ -139,30 +173,41 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = { basename = strings::dup(parsed.0), tags = parsed.2, }); - } else if (kind == "module") { + case "module" => let modhash = match (strings::next_token(&tok)) { - void => return manifest, s: str => s, + case void => + return manifest; + case s: str => + yield s; }; let modhash = match (hex::decode(modhash)) { - * => return manifest, - b: []u8 => b, + case b: []u8 => + yield b; + case => + return manifest; }; let minputs: []input = []; for (true) { let hash = match (strings::next_token(&tok)) { - void => break, - s: str => s, + case void => + break; + case s: str => + yield s; }; let hash = match (hex::decode(hash)) { - * => return manifest, - b: []u8 => b, + case b: []u8 => + yield b; + case => + return manifest; }; defer free(hash); let input = match (getinput(inputs, hash)) { - null => return manifest, - i: *input => i, + case null => + return manifest; + case i: *input => + yield i; }; append(minputs, *input); }; @@ -171,14 +216,15 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = { hash = modhash, inputs = minputs, }); - } else { + case => return manifest; }; // Check for extra tokens match (strings::next_token(&tok)) { - void => void, - s: str => return manifest, + case void => void; + case str => + return manifest; }; }; @@ -206,8 +252,10 @@ export fn current(manifest: *manifest, version: *version) bool = { }; }; let cached = match (cached) { - null => return false, - v: *version => v, + case null => + return false; + case v: *version => + yield v; }; assert(len(cached.inputs) == len(version.inputs)); diff --git a/hare/module/scan.ha b/hare/module/scan.ha @@ -30,37 +30,40 @@ export fn scan(ctx: *context, path: str) (version | error) = { }; hash::write(&sha, [if (found) 1 else 0]); let iter = match (fs::iter(ctx.fs, path)) { - fs::wrongtype => { - // Single file case - let inputs: []input = []; - let deps: []ast::ident = []; - let ft = match (type_for_ext(path)) { - void => return module_not_found, - ft: filetype => ft, - }; - let st = fs::stat(ctx.fs, path)?; - let in = input { - path = fs::resolve(ctx.fs, path), - stat = st, - ft = ft, - hash = scan_file(ctx, path, &deps)?, - ... - }; - append(inputs, in); + case fs::wrongtype => + // Single file case + let inputs: []input = []; + let deps: []ast::ident = []; + let ft = match (type_for_ext(path)) { + case void => + return module_not_found; + case ft: filetype => + yield ft; + }; + let st = fs::stat(ctx.fs, path)?; + let in = input { + path = fs::resolve(ctx.fs, path), + stat = st, + ft = ft, + hash = scan_file(ctx, path, &deps)?, + ... + }; + append(inputs, in); - let sumbuf: [sha256::SIZE]u8 = [0...]; - hash::write(&sha, in.hash); - hash::sum(&sha, sumbuf); + let sumbuf: [sha256::SIZE]u8 = [0...]; + hash::write(&sha, in.hash); + hash::sum(&sha, sumbuf); - return version { - hash = sumbuf, - basedir = path::dirname(fs::resolve(ctx.fs, path)), - depends = deps, - inputs = inputs, - }; - }, - err: fs::error => return err, - iter: *fs::iterator => iter, + return version { + hash = sumbuf, + basedir = path::dirname(fs::resolve(ctx.fs, path)), + depends = deps, + inputs = inputs, + }; + case err: fs::error => + return err; + case iter: *fs::iterator => + yield iter; }; let ver = version { basedir = strings::dup(path), @@ -93,8 +96,10 @@ fn parse_name(name: str) (str, str, []tag) = { else p: size; let tags = strings::sub(base, i, strings::end); let tags = match (parsetags(tags)) { - void => return (base, ext, []), - t: []tag => t, + case void => + return (base, ext, []); + case t: []tag => + yield t; }; let base = strings::sub(base, 0, i); return (base, ext, tags); @@ -128,14 +133,19 @@ fn scan_directory( for (true) { let ent = match (fs::next(iter)) { - void => break, - ent: fs::dirent => ent, + case void => + break; + case ent: fs::dirent => + yield ent; }; switch (ent.ftype) { - fs::mode::LINK => abort(), // TODO - fs::mode::DIR => append(dirs, strings::dup(ent.name)), - fs::mode::REG => append(files, strings::dup(ent.name)), + case fs::mode::LINK => + abort(); // TODO + case fs::mode::DIR => + append(dirs, strings::dup(ent.name)); + case fs::mode::REG => + append(files, strings::dup(ent.name)); }; }; @@ -291,8 +301,9 @@ export fn lookup(ctx: *context, name: ast::ident) (version | error) = { let cand = path::join(ctx.paths[i - 1], ipath); defer free(cand); match (scan(ctx, cand)) { - v: version => return v, - e: error => void, + case v: version => + return v; + case error => void; }; }; return module_not_found; @@ -321,9 +332,12 @@ fn scan_file( let imports = parse::imports(&lexer)?; for (let i = 0z; i < len(imports); i += 1) { let ident = match (imports[i]) { - m: ast::import_module => m: ast::ident, - a: ast::import_alias => a.ident, - o: ast::import_objects => o.ident, + case m: ast::import_module => + yield m: ast::ident; + case a: ast::import_alias => + yield a.ident; + case o: ast::import_objects => + yield o.ident; }; if (!have_ident(deps, ident)) { append(deps, ident); @@ -357,28 +371,31 @@ export fn parsetags(in: str) ([]tag | void) = { for (true) { let t = tag { ... }; let m = match (strings::next(&iter)) { - void => break, - r: rune => r, + case void => + break; + case r: rune => + yield r; }; t.mode = switch (m) { - * => { - tags_free(tags); - return; - }, - '+' => tag_mode::INCLUSIVE, - '-' => tag_mode::EXCLUSIVE, + case => + tags_free(tags); + return; + case '+' => + yield tag_mode::INCLUSIVE; + case '-' => + yield tag_mode::EXCLUSIVE; }; let buf = strio::dynamic(); for (true) match (strings::next(&iter)) { - void => break, - r: rune => { - if (ascii::isalnum(r) || r == '_') { - strio::appendrune(buf, r)!; - } else { - strings::push(&iter, r); - break; - }; - }, + case void => + break; + case r: rune => + if (ascii::isalnum(r) || r == '_') { + strio::appendrune(buf, r)!; + } else { + strings::push(&iter, r); + break; + }; }; t.name = strio::finish(buf); append(tags, t); @@ -406,8 +423,10 @@ export fn tagcompat(have: []tag, want: []tag) bool = { }; }; switch (want[i].mode) { - tag_mode::INCLUSIVE => if (!present) return false, - tag_mode::EXCLUSIVE => if (present) return false, + case tag_mode::INCLUSIVE => + if (!present) return false; + case tag_mode::EXCLUSIVE => + if (present) return false; }; }; return true; diff --git a/hare/module/types.ha b/hare/module/types.ha @@ -68,12 +68,17 @@ export type error = !( export fn strerror(err: error) const str = { // Should be more than enough for PATH_MAX * 2 static let buf: [4096]u8 = [0...]; - return match (err) { - err: fs::error => fs::strerror(err), - err: io::error => io::strerror(err), - err: parse::error => parse::strerror(err), - module_not_found => "Module not found", - amb: ambiguous => fmt::bsprintf(buf, - "Cannot choose between {} and {}", amb.0, amb.1), + match (err) { + case err: fs::error => + return fs::strerror(err); + case err: io::error => + return io::strerror(err); + case err: parse::error => + return parse::strerror(err); + case module_not_found => + return "Module not found"; + case amb: ambiguous => + return fmt::bsprintf(buf, "Cannot choose between {} and {}", + amb.0, amb.1); }; }; diff --git a/hare/parse/+test/expr.ha b/hare/parse/+test/expr.ha @@ -217,11 +217,12 @@ @test fn switch_expr() void = { roundtrip("export fn main() void = { switch (x) { - 1234, 4321 => y, - 1337 => { - z; - }, - * => q, + case 1234, 4321 => + return y; + case 1337 => + return z; + case => + return q; }; }; "); @@ -230,22 +231,26 @@ @test fn match_expr() void = { roundtrip("export fn main() void = { match (x) { - i: size => y, - str => { - z; - }, - foo => bar, - foo: int => bar, - foo::bar => baz, - null => void, - *int => void, + case i: size => + return y; + case foo => + return bar; + case foo: int => + return bar; + case foo::bar => + return baz; + case null => + void; + case *int => + void; }; match (x) { - s: matchdata => y, - str => { - z; - }, - * => q, + case s: matchdata => + return y; + case str => + return z; + case => + return q; }; }; "); diff --git a/hare/parse/+test/loc.ha b/hare/parse/+test/loc.ha @@ -12,18 +12,20 @@ fn expr_testloc(srcs: str...) void = for (let i = 0z; i < len(srcs); i += 1) { defer io::close(buf); let lexer = lex::init(buf, "<test>"); let exp = match (expression(&lexer)) { - exp: ast::expr => exp, - err: error => { - fmt::errorln(strerror(err))!; - abort(); - }, + case exp: ast::expr => + yield exp; + case err: error => + fmt::errorln(strerror(err))!; + abort(); }; defer ast::expr_free(exp); let runes = 0z; let d = utf8::decode(srcs[i]); for (true) match (utf8::next(&d)!) { - void => break, - rune => runes += 1, + case void => + break; + case rune => + runes += 1; }; assert(exp.start.line == 1 && exp.start.col == 1); assert(exp.end.line == 1 && exp.end.col == runes); @@ -55,11 +57,11 @@ fn expr_testloc(srcs: str...) void = for (let i = 0z; i < len(srcs); i += 1) { expr_testloc("if (foo) bar", "if (foo) bar else baz"); expr_testloc("len(foo)"); expr_testloc("{ foo; bar; }"); - expr_testloc("match (foo) { * => bar }"); + expr_testloc("match (foo) { case => bar; }"); expr_testloc("foo?"); expr_testloc("return foo"); expr_testloc("size(int)"); - expr_testloc("switch (foo) { * => bar }"); + expr_testloc("switch (foo) { case => bar; }"); expr_testloc("foo[bar..baz]"); expr_testloc("&foo"); @@ -69,11 +71,11 @@ fn expr_testloc(srcs: str...) void = for (let i = 0z; i < len(srcs); i += 1) { defer io::close(buf); let lexer = lex::init(buf, "<test>"); let exp = match (expression(&lexer)) { - exp: ast::expr => exp, - err: error => { - fmt::errorln(strerror(err))!; - abort(); - }, + case exp: ast::expr => + yield exp; + case err: error => + fmt::errorln(strerror(err))!; + abort(); }; defer ast::expr_free(exp); assert(exp.start.line == 1 && exp.start.col == 1); @@ -93,18 +95,20 @@ fn type_testloc(srcs: str...) void = for (let i = 0z; i < len(srcs); i += 1) { defer io::close(buf); let lexer = lex::init(buf, "<test>"); let typ = match (_type(&lexer)) { - typ: ast::_type => typ, - err: error => { - fmt::errorln(strerror(err))!; - abort(); - }, + case typ: ast::_type => + yield typ; + case err: error => + fmt::errorln(strerror(err))!; + abort(); }; defer ast::type_free(typ); let runes = 0z; let d = utf8::decode(srcs[i]); for (true) match (utf8::next(&d)!) { - void => break, - rune => runes += 1, + case void => + break; + case rune => + runes += 1; }; assert(typ.start.line == 1 && typ.start.col == 1); assert(typ.end.line == 1 && typ.end.col == runes); diff --git a/hare/parse/+test/roundtrip.ha b/hare/parse/+test/roundtrip.ha @@ -15,11 +15,11 @@ fn roundtrip(src: str) void = { let u = ast::subunit { imports = [], decls: []ast::decl = match (decls(&lexer)) { - decls: []ast::decl => decls, - err: error => { - fmt::errorln(strerror(err))!; - abort(); - }, + case decls: []ast::decl => + yield decls; + case err: error => + fmt::errorln(strerror(err))!; + abort(); }, }; defer ast::subunit_free(u); diff --git a/hare/parse/+test/unit.ha b/hare/parse/+test/unit.ha @@ -141,11 +141,11 @@ use strings; defer io::close(buf); let lexer = lex::init(buf, "<test>", lex::flags::COMMENTS); let decls = match (decls(&lexer)) { - decls: []ast::decl => decls, - err: error => { - fmt::errorln(strerror(err))!; - abort(); - }, + case decls: []ast::decl => + yield decls; + case err: error => + fmt::errorln(strerror(err))!; + abort(); }; defer for (let i = 0z; i < len(decls); i += 1) { ast::decl_free(decls[i]); diff --git a/hare/parse/decl.ha b/hare/parse/decl.ha @@ -11,16 +11,17 @@ fn attr_symbol(lexer: *lex::lexer) (str | error) = { let s = t.1 as str; let d = strings::iter(s); match (strings::next(&d)) { - void => void, - r: rune => synassert(t.2, - ascii::isalpha(r) || r == '.' || r == '_', - "Invalid symbol")?, + case void => void; + case r: rune => + synassert(t.2, ascii::isalpha(r) || r == '.' + || r == '_', "Invalid symbol")?; }; for (true) match (strings::next(&d)) { - void => break, - r: rune => synassert(t.2, - ascii::isalnum(r) || r == '$' || r == '.' || r == '_', - "Invalid symbol")?, + case void => + break; + case r: rune => + synassert(t.2, ascii::isalnum(r) || r == '$' + || r == '.' || r == '_', "Invalid symbol")?; }; want(lexer, ltok::RPAREN)?; return s; @@ -58,16 +59,20 @@ fn decl_global( let decl: []ast::decl_global = []; for (true) { const symbol = match (try(lexer, ltok::ATTR_SYMBOL)?) { - void => "", - lex::token => attr_symbol(lexer)?, + case void => + yield ""; + case lex::token => + yield attr_symbol(lexer)?; }; const ident = ident(lexer)?; want(lexer, ltok::COLON)?; const _type = _type(lexer)?; const init: nullable *ast::expr = match (try(lexer, ltok::EQUAL)?) { - lex::token => alloc(expression(lexer)?), - void => null, + case lex::token => + yield alloc(expression(lexer)?); + case void => + yield null; }; const btok = try(lexer, ltok::COMMA)?; append(decl, ast::decl_global { @@ -77,9 +82,8 @@ fn decl_global( _type = _type, init = init, }); - match (btok) { - void => break, - * => void, + if (btok is void) { + break; }; }; return decl; @@ -96,9 +100,8 @@ fn decl_type(lexer: *lex::lexer) ([]ast::decl_type | error) = { ident = ident, _type = _type, }); - match (btok) { - void => break, - * => void, + if (btok is void) { + break; }; }; return decl; @@ -111,15 +114,23 @@ fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = { ltok::ATTR_NORETURN, ltok::ATTR_SYMBOL ]; for (true) match (try(lexer, attrs...)?) { - void => break, - t: lex::token => switch (t.0) { - ltok::ATTR_FINI => attr = ast::fndecl_attrs::FINI, - ltok::ATTR_INIT => attr = ast::fndecl_attrs::INIT, - ltok::ATTR_TEST => attr = ast::fndecl_attrs::TEST, - ltok::ATTR_NORETURN => noreturn = true, - ltok::ATTR_SYMBOL => sym = attr_symbol(lexer)?, - * => abort("unreachable"), - }, + case void => + break; + case t: lex::token => + switch (t.0) { + case ltok::ATTR_FINI => + attr = ast::fndecl_attrs::FINI; + case ltok::ATTR_INIT => + attr = ast::fndecl_attrs::INIT; + case ltok::ATTR_TEST => + attr = ast::fndecl_attrs::TEST; + case ltok::ATTR_NORETURN => + noreturn = true; + case ltok::ATTR_SYMBOL => + sym = attr_symbol(lexer)?; + case => + abort("unreachable"); + }; }; want(lexer, ltok::FN)?; @@ -134,18 +145,18 @@ fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = { let tok = want(lexer, ltok::EQUAL, ltok::SEMICOLON)?; let body = switch (tok.0) { - ltok::EQUAL => { - synassert(ident_loc, len(ident) == 1, - "Unexpected identifier, was expecting name")?; - const params = prototype.params; - for (let i = 0z; i < len(params); i += 1) { - synassert(params[i].loc, - len(params[i].name) > 0, - "Expected parameter name in function declaration")?; - }; - yield expression(lexer)?; - }, - ltok::SEMICOLON => lex::unlex(lexer, tok), + case ltok::EQUAL => + synassert(ident_loc, len(ident) == 1, + "Unexpected identifier, was expecting name")?; + const params = prototype.params; + for (let i = 0z; i < len(params); i += 1) { + synassert(params[i].loc, + len(params[i].name) > 0, + "Expected parameter name in function declaration")?; + }; + yield expression(lexer)?; + case ltok::SEMICOLON => + yield lex::unlex(lexer, tok); }; return ast::decl_func { @@ -169,11 +180,11 @@ export fn decls(lexer: *lex::lexer) ([]ast::decl | error) = { if (peek(lexer, ltok::EOF)? is lex::token) break; let comment = ""; let exported = match (try(lexer, ltok::EXPORT)?) { - void => false, - lex::token => { - comment = strings::dup(lex::comment(lexer)); - yield true; - }, + case void => + yield false; + case lex::token => + comment = strings::dup(lex::comment(lexer)); + yield true; }; const toks = [ltok::CONST, ltok::LET, ltok::DEF, ltok::TYPE]; const next = try(lexer, toks...)?; @@ -181,14 +192,18 @@ export fn decls(lexer: *lex::lexer) ([]ast::decl | error) = { comment = strings::dup(lex::comment(lexer)); }; let decl = match (next) { - void => decl_func(lexer)?, - t: lex::token => switch (t.0) { - ltok::TYPE => decl_type(lexer)?, - ltok::LET, ltok::CONST => - decl_global(lexer, t.0)?, - ltok::DEF => decl_const(lexer, t.0)?, - * => abort(), - }, + case void => + yield decl_func(lexer)?; + case t: lex::token => + yield switch (t.0) { + case ltok::TYPE => + yield decl_type(lexer)?; + case ltok::LET, ltok::CONST => + yield decl_global(lexer, t.0)?; + case ltok::DEF => + yield decl_const(lexer, t.0)?; + case => abort(); + }; }; append(decls, ast::decl { exported = exported, diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha @@ -6,10 +6,7 @@ use strings; // Parses an expression. export fn expression(lexer: *lex::lexer) (ast::expr | error) = { const loc = lex::mkloc(lexer); - const indirect = match (try(lexer, ltok::TIMES)?) { - void => false, - lex::token => true, - }; + const indirect = try(lexer, ltok::TIMES)? is lex::token; // All assignment-op tokens const atoks: []ltok = [ @@ -39,25 +36,35 @@ export fn expression(lexer: *lex::lexer) (ast::expr | error) = { ltok::SWITCH, ltok::IF, ltok::LABEL, ltok::FOR, ltok::BREAK, ltok::CONTINUE, ltok::RETURN, ltok::LET, ltok::CONST, ltok::YIELD)?) { - void => binarithm(lexer, void, 0)?, - tok: lex::token => switch (tok.0) { - ltok::LABEL, ltok::LBRACE => compound_expr(lexer)?, - ltok::MATCH => match_expr(lexer)?, - ltok::SWITCH => switch_expr(lexer)?, - ltok::IF => if_expr(lexer)?, - ltok::FOR => for_expr(lexer)?, - ltok::BREAK, - ltok::CONTINUE, - ltok::RETURN => control(lexer)?, - ltok::LET, ltok::CONST => binding(lexer, false)?, - ltok::YIELD => yield_expr(lexer)?, - * => abort(), // Invariant - }, + case void => + yield binarithm(lexer, void, 0)?; + case tok: lex::token => + yield switch (tok.0) { + case ltok::LABEL, ltok::LBRACE => + yield compound_expr(lexer)?; + case ltok::MATCH => + yield match_expr(lexer)?; + case ltok::SWITCH => + yield switch_expr(lexer)?; + case ltok::IF => + yield if_expr(lexer)?; + case ltok::FOR => + yield for_expr(lexer)?; + case ltok::BREAK, ltok::CONTINUE, ltok::RETURN => + yield control(lexer)?; + case ltok::LET, ltok::CONST => + yield binding(lexer, false)?; + case ltok::YIELD => + yield yield_expr(lexer)?; + case => abort(); // Invariant + }; }; const tok = match (try(lexer, atoks...)?) { - tok: lex::token => tok, - * => return expr, + case tok: lex::token => + yield tok; + case => + return expr; }; synassert(lex::mkloc(lexer), @@ -65,20 +72,34 @@ export fn expression(lexer: *lex::lexer) (ast::expr | error) = { "Expected an object-selector or slice for assignment target")?; const expr = ast::assign_expr { op = switch (tok.0) { - ltok::EQUAL => void, - ltok::BANDEQ => ast::binarithm_op::BAND, - ltok::BOREQ => ast::binarithm_op::BOR, - ltok::BXOREQ => ast::binarithm_op::BXOR, - ltok::DIVEQ => ast::binarithm_op::DIV, - ltok::LANDEQ => ast::binarithm_op::LAND, - ltok::LOREQ => ast::binarithm_op::LOR, - ltok::LSHIFTEQ => ast::binarithm_op::LSHIFT, - ltok::LXOREQ => ast::binarithm_op::LXOR, - ltok::MINUSEQ => ast::binarithm_op::MINUS, - ltok::MODEQ => ast::binarithm_op::MODULO, - ltok::PLUSEQ => ast::binarithm_op::PLUS, - ltok::RSHIFTEQ => ast::binarithm_op::RSHIFT, - ltok::TIMESEQ => ast::binarithm_op::TIMES, + case ltok::EQUAL => + yield void; + case ltok::BANDEQ => + yield ast::binarithm_op::BAND; + case ltok::BOREQ => + yield ast::binarithm_op::BOR; + case ltok::BXOREQ => + yield ast::binarithm_op::BXOR; + case ltok::DIVEQ => + yield ast::binarithm_op::DIV; + case ltok::LANDEQ => + yield ast::binarithm_op::LAND; + case ltok::LOREQ => + yield ast::binarithm_op::LOR; + case ltok::LSHIFTEQ => + yield ast::binarithm_op::LSHIFT; + case ltok::LXOREQ => + yield ast::binarithm_op::LXOR; + case ltok::MINUSEQ => + yield ast::binarithm_op::MINUS; + case ltok::MODEQ => + yield ast::binarithm_op::MODULO; + case ltok::PLUSEQ => + yield ast::binarithm_op::PLUS; + case ltok::RSHIFTEQ => + yield ast::binarithm_op::RSHIFT; + case ltok::TIMESEQ => + yield ast::binarithm_op::TIMES; }, object = alloc(expr), value = alloc(expression(lexer)?), @@ -96,37 +117,41 @@ fn assert_expr(lexer: *lex::lexer, is_static: bool) (ast::expr | error) = { const tok = want(lexer, ltok::ABORT, ltok::ASSERT)?; let expr = switch (tok.0) { - ltok::ABORT => { - want(lexer, ltok::LPAREN)?; - const msg: nullable *ast::expr = - if (peek(lexer, ltok::RPAREN)? is lex::token) { - yield null; - } else alloc(expression(lexer)?); - want(lexer, ltok::RPAREN)?; - - yield ast::assert_expr { - cond = null, - message = msg, - is_static = is_static, + case ltok::ABORT => + want(lexer, ltok::LPAREN)?; + const msg: nullable *ast::expr = + match (peek(lexer, ltok::RPAREN)?) { + case lex::token => + yield null; + case => + yield alloc(expression(lexer)?); }; - }, - ltok::ASSERT => { - want(lexer, ltok::LPAREN)?; - const cond: nullable *ast::expr = - alloc(expression(lexer)?); - const msg: nullable *ast::expr = - if (try(lexer, ltok::COMMA)? is lex::token) { - yield alloc(expression(lexer)?); - } else null; - want(lexer, ltok::RPAREN)?; + want(lexer, ltok::RPAREN)?; - yield ast::assert_expr { - cond = cond, - message = msg, - is_static = is_static, + yield ast::assert_expr { + cond = null, + message = msg, + is_static = is_static, + }; + case ltok::ASSERT => + want(lexer, ltok::LPAREN)?; + const cond: nullable *ast::expr = + alloc(expression(lexer)?); + const msg: nullable *ast::expr = + match (try(lexer, ltok::COMMA)?) { + case lex::token => + yield alloc(expression(lexer)?); + case => + yield null; }; - }, - * => abort(), // unreachable + want(lexer, ltok::RPAREN)?; + + yield ast::assert_expr { + cond = cond, + message = msg, + is_static = is_static, + }; + case => abort(); // unreachable }; return ast::expr { @@ -177,24 +202,23 @@ fn append_insert_expr( const expr = alloc(expression(lexer)?); switch (want(lexer, ltok::COMMA, ltok::ELLIPSIS, ltok::RPAREN)?.0) { - ltok::COMMA => append(values, expr), - ltok::ELLIPSIS => { - variadic = expr; - try(lexer, ltok::COMMA)?; - want(lexer, ltok::RPAREN)?; - break; - }, - ltok::RPAREN => { - append(values, expr); - break; - }, - * => abort(), + case ltok::COMMA => + append(values, expr); + case ltok::ELLIPSIS => + variadic = expr; + try(lexer, ltok::COMMA)?; + want(lexer, ltok::RPAREN)?; + break; + case ltok::RPAREN => + append(values, expr); + break; + case => abort(); }; }; synassert(lex::mkloc(lexer), variadic != null || len(values) != 0, "Expected values to append")?; - + const expr = if (tok.0 == ltok::APPEND) ast::append_expr { object = alloc(object), variadic = variadic, @@ -217,21 +241,18 @@ fn append_insert_expr( fn measurement(lexer: *lex::lexer) (ast::expr | error) = { const tok = want(lexer, ltok::LEN, ltok::SIZE, ltok::OFFSET)?; const expr = switch (tok.0) { - ltok::LEN => { - want(lexer, ltok::LPAREN)?; - let e = expression(lexer)?; - want(lexer, ltok::RPAREN)?; - - yield alloc(e): ast::len_expr; - }, - ltok::SIZE => { - want(lexer, ltok::LPAREN)?; - let ty = _type(lexer)?; - want(lexer, ltok::RPAREN)?; - - yield alloc(ty): ast::size_expr; - }, - ltok::OFFSET => abort(), // TODO + case ltok::LEN => + want(lexer, ltok::LPAREN)?; + let e = expression(lexer)?; + want(lexer, ltok::RPAREN)?; + yield alloc(e): ast::len_expr; + case ltok::SIZE => + want(lexer, ltok::LPAREN)?; + let ty = _type(lexer)?; + want(lexer, ltok::RPAREN)?; + yield alloc(ty): ast::size_expr; + case ltok::OFFSET => + abort(); // TODO }; return ast::expr { @@ -249,8 +270,10 @@ fn binarithm( // Precedence climbing parser // https://en.wikipedia.org/wiki/Operator-precedence_parser let lvalue = match (lvalue) { - void => cast(lexer, void)?, - expr: ast::expr => expr, + case void => + yield cast(lexer, void)?; + case expr: ast::expr => + yield expr; }; let tok = lex::lex(lexer)?; @@ -284,10 +307,7 @@ fn binarithm( fn binding(lexer: *lex::lexer, is_static: bool) (ast::expr | error) = { const loc = lex::mkloc(lexer); - const is_const = switch (want(lexer, ltok::LET, ltok::CONST)?.0) { - ltok::LET => false, - ltok::CONST => true, - }; + const is_const = want(lexer, ltok::LET, ltok::CONST)?.0 == ltok::CONST; let bindings: []ast::binding = []; for (true) { @@ -304,8 +324,8 @@ fn binding(lexer: *lex::lexer, is_static: bool) (ast::expr | error) = { init = init, }); match (try(lexer, ltok::COMMA)?) { - void => break, - lex::token => void, + case void => break; + case lex::token => void; }; }; @@ -322,49 +342,58 @@ fn binding(lexer: *lex::lexer, is_static: bool) (ast::expr | error) = { fn builtin(lexer: *lex::lexer) (ast::expr | error) = { const tok = match (peek(lexer, ltok::ALLOC, ltok::APPEND, ltok::FREE, - ltok::DELETE, ltok::ABORT, ltok::ASSERT, ltok::INSERT, - ltok::STATIC, ltok::SIZE, ltok::LEN, ltok::OFFSET, - ltok::DEFER)?) { - tok: lex::token => tok, - void => return postfix(lexer, void), + ltok::DELETE, ltok::ABORT, ltok::ASSERT, ltok::INSERT, + ltok::STATIC, ltok::SIZE, ltok::LEN, ltok::OFFSET, + ltok::DEFER)?) { + case tok: lex::token => + yield tok; + case void => + return postfix(lexer, void); }; return switch (tok.0) { - ltok::ALLOC => alloc_expr(lexer), - ltok::APPEND, ltok::INSERT => append_insert_expr(lexer, false), - ltok::DELETE => delete_expr(lexer, false), - ltok::FREE => free_expr(lexer), - ltok::ABORT, ltok::ASSERT => assert_expr(lexer, false), - ltok::STATIC => { - want(lexer, ltok::STATIC)?; - let tok = match (peek(lexer, ltok::LET, ltok::CONST, - ltok::ABORT, ltok::ASSERT, ltok::APPEND, - ltok::INSERT, ltok::DELETE)?) { - tok: lex::token => tok, - // TODO: The following is lame - void => return syntaxerr(tok.2, - "Expected let, const, or assert"), - }; - yield switch (tok.0) { - ltok::LET, ltok::CONST => binding(lexer, true), - ltok::ABORT, - ltok::ASSERT => assert_expr(lexer, true), - ltok::APPEND, - ltok::INSERT => append_insert_expr(lexer, true), - ltok::DELETE => delete_expr(lexer, true), - * => abort(), - }; - }, - ltok::SIZE, ltok::LEN, ltok::OFFSET => measurement(lexer), - ltok::DEFER => { - want(lexer, ltok::DEFER)?; - let expr = alloc(expression(lexer)?); - yield ast::expr { - start = tok.2, - end = lex::prevloc(lexer), - expr = expr: ast::defer_expr, - }; - }, - * => abort(), // Invariant + case ltok::ALLOC => + yield alloc_expr(lexer); + case ltok::APPEND, ltok::INSERT => + yield append_insert_expr(lexer, false); + case ltok::DELETE => + yield delete_expr(lexer, false); + case ltok::FREE => + yield free_expr(lexer); + case ltok::ABORT, ltok::ASSERT => + yield assert_expr(lexer, false); + case ltok::STATIC => + want(lexer, ltok::STATIC)?; + let tok = match (peek(lexer, ltok::LET, ltok::CONST, + ltok::ABORT, ltok::ASSERT, ltok::APPEND, ltok::INSERT, + ltok::DELETE)?) { + case tok: lex::token => + yield tok; + case void => + // TODO: The following is lame: + return syntaxerr(tok.2, "Expected let, const, or assert"); + }; + yield switch (tok.0) { + case ltok::LET, ltok::CONST => + yield binding(lexer, true); + case ltok::ABORT, ltok::ASSERT => + yield assert_expr(lexer, true); + case ltok::APPEND, ltok::INSERT => + yield append_insert_expr(lexer, true); + case ltok::DELETE => + yield delete_expr(lexer, true); + case => abort(); + }; + case ltok::SIZE, ltok::LEN, ltok::OFFSET => + yield measurement(lexer); + case ltok::DEFER => + want(lexer, ltok::DEFER)?; + let expr = alloc(expression(lexer)?); + yield ast::expr { + start = tok.2, + end = lex::prevloc(lexer), + expr = expr: ast::defer_expr, + }; + case => abort(); // Invariant }; }; @@ -374,25 +403,24 @@ fn call(lexer: *lex::lexer, lvalue: ast::expr) (ast::expr | error) = { for (true) { match (try(lexer, ltok::RPAREN)?) { - lex::token => break, - void => void, + case lex::token => break; + case void => void; }; append(args, alloc(expression(lexer)?)); match (try(lexer, ltok::ELLIPSIS)?) { - lex::token => { - variadic = true; - try(lexer, ltok::COMMA)?; - want(lexer, ltok::RPAREN)?; - break; - }, - void => void, + case lex::token => + variadic = true; + try(lexer, ltok::COMMA)?; + want(lexer, ltok::RPAREN)?; + break; + case void => void; }; switch (want(lexer, ltok::COMMA, ltok::RPAREN)?.0) { - ltok::RPAREN => break, - * => void, + case ltok::RPAREN => break; + case => void; }; }; @@ -409,19 +437,25 @@ fn call(lexer: *lex::lexer, lvalue: ast::expr) (ast::expr | error) = { fn cast(lexer: *lex::lexer, lvalue: (ast::expr | void)) (ast::expr | error) = { const lvalue = match (lvalue) { - void => unarithm(lexer)?, - e: ast::expr => e, + case void => + yield unarithm(lexer)?; + case e: ast::expr => + yield e; }; - const tok = match (try(lexer, ltok::COLON, - ltok::AS, ltok::IS)?) { - void => return lvalue, - tok: lex::token => tok.0, + const tok = match (try(lexer, ltok::COLON, ltok::AS, ltok::IS)?) { + case void => + return lvalue; + case tok: lex::token => + yield tok.0; }; const kind = switch (tok) { - ltok::COLON => ast::cast_kind::CAST, - ltok::AS => ast::cast_kind::ASSERTION, - ltok::IS => ast::cast_kind::TEST, - * => abort(), + case ltok::COLON => + yield ast::cast_kind::CAST; + case ltok::AS => + yield ast::cast_kind::ASSERTION; + case ltok::IS => + yield ast::cast_kind::TEST; + case => abort(); }; let typ = alloc(_type(lexer)?); return cast(lexer, ast::expr { @@ -438,16 +472,22 @@ fn cast(lexer: *lex::lexer, lvalue: (ast::expr | void)) (ast::expr | error) = { fn constant(lexer: *lex::lexer) (ast::expr | error) = { const tok = want(lexer)?; const expr: ast::value = switch (tok.0) { - ltok::LIT_U8, ltok::LIT_U16, ltok::LIT_U32, ltok::LIT_U64, + case ltok::LIT_U8, ltok::LIT_U16, ltok::LIT_U32, ltok::LIT_U64, ltok::LIT_UINT, ltok::LIT_SIZE, ltok::LIT_I8, ltok::LIT_I16, ltok::LIT_I32, ltok::LIT_I64, ltok::LIT_INT, ltok::LIT_ICONST, ltok::LIT_F32, ltok::LIT_F64, ltok::LIT_FCONST, ltok::LIT_RUNE, - ltok::LIT_STR => tok.1, - ltok::VOID => void, - ltok::TRUE => true, - ltok::FALSE => false, - ltok::NULL => ast::_null, - * => return syntaxerr(lex::mkloc(lexer), "Expected constant expression"), + ltok::LIT_STR => + yield tok.1; + case ltok::VOID => + yield void; + case ltok::TRUE => + yield true; + case ltok::FALSE => + yield false; + case ltok::NULL => + yield ast::_null; + case => + return syntaxerr(lex::mkloc(lexer), "Expected constant expression"); }; return ast::expr { start = tok.2, @@ -460,17 +500,24 @@ fn control(lexer: *lex::lexer) (ast::expr | error) = { let tok = want(lexer, ltok::BREAK, ltok::CONTINUE, ltok::RETURN)?; let label = if (tok.0 == ltok::BREAK || tok.0 == ltok::CONTINUE) { yield match (try(lexer, ltok::LABEL)?) { - tok: lex::token => tok.1 as str, - void => "", + case tok: lex::token => + yield tok.1 as str; + case void => + yield ""; }; } else ""; const expr = switch (tok.0) { - ltok::BREAK => label: ast::break_expr, - ltok::CONTINUE => label: ast::continue_expr, - ltok::RETURN => match (peek(lexer, ltok::COMMA, ltok::SEMICOLON)?) { - void => alloc(expression(lexer)?): ast::return_expr, - lex::token => null: ast::return_expr, - }, + case ltok::BREAK => + yield label: ast::break_expr; + case ltok::CONTINUE => + yield label: ast::continue_expr; + case ltok::RETURN => + yield match (peek(lexer, ltok::COMMA, ltok::SEMICOLON)?) { + case void => + yield alloc(expression(lexer)?): ast::return_expr; + case lex::token => + yield null: ast::return_expr; + }; }; return ast::expr { start = tok.2, @@ -500,17 +547,18 @@ fn compound_expr(lexer: *lex::lexer) (ast::expr | error) = { const start = want(lexer, ltok::LBRACE, ltok::LABEL)?; const label = switch (start.0) { - ltok::LABEL => { - want(lexer, ltok::LBRACE)?; - yield start.1 as str; - }, - * => "", + case ltok::LABEL => + want(lexer, ltok::LBRACE)?; + yield start.1 as str; + case => + yield ""; }; for (let more = true; more) { const item = match (peek(lexer, ltok::RBRACE)?) { - lex::token => break, - void => expression(lexer)?, + case lex::token => break; + case void => + yield expression(lexer)?; }; append(items, alloc(item)); want(lexer, ltok::SEMICOLON)?; @@ -531,31 +579,28 @@ fn for_expr(lexer: *lex::lexer) (ast::expr | error) = { const tok = want(lexer, ltok::FOR)?; want(lexer, ltok::LPAREN)?; - const bindings: nullable *ast::expr = match (peek( - lexer, ltok::LET, ltok::CONST)?) { - void => null, - lex::token => { + const bindings: nullable *ast::expr = + match (peek(lexer, ltok::LET, ltok::CONST)?) { + case void => + yield null; + case lex::token => const bindings = alloc(binding(lexer, false)?); want(lexer, ltok::SEMICOLON)?; yield bindings; - }, - }; - + }; const cond = alloc(expression(lexer)?); - - const afterthought: nullable *ast::expr = match (peek( - lexer, ltok::SEMICOLON)) { - void => null, - lex::token => { + const afterthought: nullable *ast::expr = + match (peek(lexer, ltok::SEMICOLON)) { + case void => + yield null; + case lex::token => want(lexer, ltok::SEMICOLON)?; yield alloc(expression(lexer)?); - }, - }; + }; want(lexer, ltok::RPAREN)?; const body = alloc(expression(lexer)?); - return ast::expr { start = tok.2, end = lex::prevloc(lexer), @@ -587,8 +632,10 @@ fn if_expr(lexer: *lex::lexer) (ast::expr | error) = { want(lexer, ltok::RPAREN)?; const tbranch = alloc(expression(lexer)?); const fbranch: nullable *ast::expr = match (try(lexer, ltok::ELSE)?) { - void => null, - lex::token => alloc(expression(lexer)?), + case void => + yield null; + case lex::token => + yield alloc(expression(lexer)?); }; return ast::expr { start = start.2, @@ -605,17 +652,16 @@ 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; - match (try(lexer, ltok::SLICE)?) { - void => start = alloc(expression(lexer)?), - lex::token => is_slice = true, + if (try(lexer, ltok::SLICE)? is lex::token) { + is_slice = true; + } else { + start = alloc(expression(lexer)?); }; - if (!is_slice) match (try(lexer, ltok::SLICE)?) { - void => void, - lex::token => is_slice = true, + if (!is_slice && try(lexer, ltok::SLICE)? is lex::token) { + is_slice = true; }; - if (is_slice) match (peek(lexer, ltok::RBRACKET)?) { - lex::token => void, - void => end = alloc(expression(lexer)?), + if (is_slice && peek(lexer, ltok::RBRACKET)? is void) { + end = alloc(expression(lexer)?); }; want(lexer, ltok::RBRACKET)?; @@ -648,48 +694,49 @@ fn plain_expression(lexer: *lex::lexer) (ast::expr | error) = { if (tok.0 >= ltok::LIT_U8 && tok.0 <= ltok::LAST_LITERAL) { return constant(lexer); }; - return switch (tok.0) { - ltok::TRUE, ltok::FALSE, ltok::NULL, ltok::VOID => - constant(lexer), - ltok::LBRACKET => plain_array(lexer)?, - ltok::STRUCT => { - let s = plain_struct(lexer, [])?; + switch (tok.0) { + case ltok::TRUE, ltok::FALSE, ltok::NULL, ltok::VOID => + return constant(lexer); + case ltok::LBRACKET => + return plain_array(lexer)?; + case ltok::STRUCT => + let s = plain_struct(lexer, [])?; + return ast::expr { + start = tok.2, + end = lex::prevloc(lexer), + expr = s, + }; + case ltok::LPAREN => + want(lexer, ltok::LPAREN)?; + let ex = expression(lexer)?; + switch (want(lexer, ltok::RPAREN, ltok::COMMA)?.0) { + case ltok::RPAREN => + return ex; + case ltok::COMMA => + return plain_tuple(lexer, ex, tok.2)?; + case => abort(); + }; + case ltok::NAME => + let id = ident(lexer)?; + match (peek(lexer, ltok::LBRACE)?) { + case void => return ast::expr { start = tok.2, end = lex::prevloc(lexer), - expr = s, + expr = id: ast::access_identifier, }; - }, - ltok::LPAREN => { - want(lexer, ltok::LPAREN)?; - let ex = expression(lexer)?; - return switch (want(lexer, ltok::RPAREN, ltok::COMMA)?.0) { - ltok::RPAREN => ex, - ltok::COMMA => plain_tuple(lexer, ex, tok.2)?, - * => abort(), - }; - }, - ltok::NAME => { - let id = ident(lexer)?; - return match (peek(lexer, ltok::LBRACE)?) { - void => ast::expr { - start = tok.2, - end = lex::prevloc(lexer), - expr = id: ast::access_identifier, - }, - lex::token => { - let s = plain_struct(lexer, id)?; - yield ast::expr { - start = tok.2, - end = lex::prevloc(lexer), - expr = s, - }; - }, + case lex::token => + let s = plain_struct(lexer, id)?; + return ast::expr { + start = tok.2, + end = lex::prevloc(lexer), + expr = s, }; - }, - * => syntaxerr(lex::mkloc(lexer), + }; + case => + return syntaxerr(lex::mkloc(lexer), "Unexpected {}, was expecting an expression", - lex::tokstr(tok)), + lex::tokstr(tok)); }; }; @@ -700,27 +747,26 @@ fn plain_array(lexer: *lex::lexer) (ast::expr | error) = { let expand = false; for (true) { match (try(lexer, ltok::RBRACKET)?) { - lex::token => break, - void => void, + case lex::token => break; + case void => void; }; append(values, alloc(expression(lexer)?)); match (try(lexer, ltok::COMMA, ltok::ELLIPSIS)?) { - void => { + case void => + want(lexer, ltok::RBRACKET)?; + break; + case tok: lex::token => + switch (tok.0) { + case ltok::ELLIPSIS => + expand = true; + try(lexer, ltok::COMMA)?; want(lexer, ltok::RBRACKET)?; break; - }, - tok: lex::token => switch (tok.0) { - ltok::COMMA => void, - ltok::ELLIPSIS => { - expand = true; - try(lexer, ltok::COMMA)?; - want(lexer, ltok::RBRACKET)?; - break; - }, - * => abort(), - }, + case ltok::COMMA => void; + case => abort(); + }; }; }; return ast::expr { @@ -748,27 +794,24 @@ fn plain_struct( const tok = want(lexer, ltok::ELLIPSIS, ltok::NAME, ltok::STRUCT)?; switch (tok.0) { - ltok::ELLIPSIS => { - synassert(lex::mkloc(lexer), len(alias) != 0, - "Cannot use auto-fill with anonymous struct")?; - autofill = true; - try(lexer, ltok::COMMA)?; - want(lexer, ltok::RBRACE)?; - break; - }, - ltok::NAME, ltok::STRUCT => { - lex::unlex(lexer, tok); - append(fields, struct_field(lexer)?); - }, + case ltok::ELLIPSIS => + synassert(lex::mkloc(lexer), len(alias) != 0, + "Cannot use auto-fill with anonymous struct")?; + autofill = true; + try(lexer, ltok::COMMA)?; + want(lexer, ltok::RBRACE)?; + break; + case ltok::NAME, ltok::STRUCT => + lex::unlex(lexer, tok); + append(fields, struct_field(lexer)?); }; switch (want(lexer, ltok::COMMA, ltok::RBRACE)?.0) { - ltok::COMMA => { - if (try(lexer, ltok::RBRACE) is lex::token) { - break; - }; - }, - ltok::RBRACE => break, + case ltok::RBRACE => break; + case ltok::COMMA => + if (try(lexer, ltok::RBRACE) is lex::token) { + break; + }; }; }; @@ -784,46 +827,44 @@ fn struct_field( ) (ast::struct_value | *ast::struct_constant | error) = { const tok = want(lexer, ltok::NAME, ltok::STRUCT)?; switch (tok.0) { - ltok::NAME => { - const name = strings::dup(tok.1 as str); - const tok = match (try(lexer, ltok::COLON, - ltok::DOUBLE_COLON, ltok::EQUAL)?) { - tok: lex::token => tok, - void => { - let id: ast::ident = alloc([name]); - return alloc(plain_struct(lexer, id)?); - }, + case ltok::NAME => + const name = strings::dup(tok.1 as str); + const tok = match (try(lexer, ltok::COLON, + ltok::DOUBLE_COLON, ltok::EQUAL)?) { + case tok: lex::token => + yield tok; + case void => + let id: ast::ident = alloc([name]); + return alloc(plain_struct(lexer, id)?); + }; + + switch (tok.0) { + case ltok::COLON => + const _type = alloc(_type(lexer)?); + want(lexer, ltok::EQUAL)?; + const init = alloc(expression(lexer)?); + return ast::struct_value { + name = name, + _type = _type, + init = init, }; - return switch (tok.0) { - ltok::COLON => { - const _type = alloc(_type(lexer)?); - want(lexer, ltok::EQUAL)?; - const init = alloc(expression(lexer)?); - yield ast::struct_value { - name = name, - _type = _type, - init = init, - }; - }, - ltok::DOUBLE_COLON => { - let id: ast::ident = alloc([name]); - let rest = ident(lexer)?; - append(id, rest...); - return alloc(plain_struct(lexer, id)?); - }, - ltok::EQUAL => ast::struct_value { - name = name, - _type = null, - init = alloc(expression(lexer)?), - }, - * => abort(), // Invariant + case ltok::DOUBLE_COLON => + let id: ast::ident = alloc([name]); + let rest = ident(lexer)?; + append(id, rest...); + return alloc(plain_struct(lexer, id)?); + case ltok::EQUAL => + return ast::struct_value { + name = name, + _type = null, + init = alloc(expression(lexer)?), }; - }, - ltok::STRUCT => { - lex::unlex(lexer, tok); - return alloc(plain_struct(lexer, [])?); - }, - * => abort(), // Invariant + case => abort(); // Invariant + }; + case ltok::STRUCT => + lex::unlex(lexer, tok); + return alloc(plain_struct(lexer, [])?); + case => abort(); // Invariant }; }; @@ -837,18 +878,17 @@ fn plain_tuple( for (true) { match (try(lexer, ltok::RPAREN)?) { - lex::token => break, - void => void, + case lex::token => break; + case void => void; }; append(values, alloc(expression(lexer)?)); match (try(lexer, ltok::COMMA)?) { - void => { - want(lexer, ltok::RPAREN)?; - break; - }, - lex::token => void, + case lex::token => void; + case void => + want(lexer, ltok::RPAREN)?; + break; }; }; @@ -862,51 +902,66 @@ fn plain_tuple( fn postfix(lexer: *lex::lexer, lvalue: (ast::expr | void)) (ast::expr | error) = { let lvalue = match (lvalue) { - void => plain_expression(lexer)?, - ex: ast::expr => ex, - }; - - return postfix(lexer, match (try(lexer, ltok::LPAREN, - ltok::DOT, ltok::LBRACKET, ltok::QUESTION, ltok::LNOT)) { - tok: lex::token => switch (tok.0) { - ltok::LPAREN => call(lexer, lvalue)?, - ltok::DOT => postfix_dot(lexer, lvalue)?, - ltok::LBRACKET => indexing(lexer, lvalue)?, - ltok::QUESTION => ast::expr { - start = lvalue.start, - end = lex::prevloc(lexer), - expr = ast::propagate_expr { - is_abort = false, - expr = alloc(lvalue), - }, + case void => + yield plain_expression(lexer)?; + case ex: ast::expr => + yield ex; + }; + + let tok = match (try(lexer, ltok::LPAREN, ltok::DOT, + ltok::LBRACKET, ltok::QUESTION, ltok::LNOT)) { + case void => + return lvalue; + case tok: lex::token => + yield tok; + }; + + let next = switch (tok.0) { + case ltok::LPAREN => + yield call(lexer, lvalue)?; + case ltok::DOT => + yield postfix_dot(lexer, lvalue)?; + case ltok::LBRACKET => + yield indexing(lexer, lvalue)?; + case ltok::QUESTION => + yield ast::expr { + start = lvalue.start, + end = lex::prevloc(lexer), + expr = ast::propagate_expr { + is_abort = false, + expr = alloc(lvalue), }, - ltok::LNOT => ast::expr { - start = lvalue.start, - end = lex::prevloc(lexer), - expr = ast::propagate_expr { - is_abort = true, - expr = alloc(lvalue), - }, + }; + case ltok::LNOT => + yield ast::expr { + start = lvalue.start, + end = lex::prevloc(lexer), + expr = ast::propagate_expr { + is_abort = true, + expr = alloc(lvalue), }, - * => abort(), - }, - void => return lvalue, - }); + }; + case => abort(); + }; + + return postfix(lexer, next); }; fn postfix_dot( lexer: *lex::lexer, lvalue: ast::expr, -) (ast::expr | error) = match (try(lexer, ltok::NAME)?) { - tok: lex::token => ast::expr { - start = lvalue.start, - end = lex::prevloc(lexer), - expr = ast::access_field { - object = alloc(lvalue), - field = tok.1 as str, - }, - }, - void => { +) (ast::expr | error) = { + match (try(lexer, ltok::NAME)?) { + case tok: lex::token => + return ast::expr { + start = lvalue.start, + end = lex::prevloc(lexer), + expr = ast::access_field { + object = alloc(lvalue), + field = tok.1 as str, + }, + }; + case void => let con = constant(lexer)?; let val = con.expr as ast::constant_expr; synassert(lex::mkloc(lexer), val is ast::value, @@ -914,7 +969,7 @@ fn postfix_dot( let val = val as ast::value; synassert(lex::mkloc(lexer), val is i64, "Expected integer constant")?; - yield ast::expr { + return ast::expr { start = lvalue.start, end = lex::prevloc(lexer), expr = ast::access_tuple { @@ -922,7 +977,7 @@ fn postfix_dot( value = alloc(con), }, }; - }, + }; }; fn switch_expr(lexer: *lex::lexer) (ast::expr | error) = { @@ -936,19 +991,15 @@ fn switch_expr(lexer: *lex::lexer) (ast::expr | error) = { let cases: []ast::switch_case = []; for (true) { - if (try(lexer, ltok::RBRACE)? is lex::token) break; + want(lexer, ltok::CASE)?; let opts: []*ast::expr = []; - if (try(lexer, ltok::TIMES)? is lex::token) { - want(lexer, ltok::CASE)?; - } else { + if (try(lexer, ltok::ARROW)? is void) { for (true) { - if (try(lexer, ltok::CASE) is lex::token) break; - append(opts, alloc(expression(lexer)?)); - if (!(try(lexer, ltok::COMMA) is lex::token)) { - want(lexer, ltok::CASE)?; + if (try(lexer, ltok::COMMA)? is void) { + want(lexer, ltok::ARROW)?; break; }; }; @@ -956,13 +1007,18 @@ fn switch_expr(lexer: *lex::lexer) (ast::expr | error) = { "Expected a list of options")?; }; + let exprs: []*ast::expr = []; + for (peek(lexer, ltok::CASE, ltok::RBRACE)? is void) { + append(exprs, alloc(expression(lexer)?)); + want(lexer, ltok::SEMICOLON)?; + }; + append(cases, ast::switch_case { options = opts, - value = alloc(expression(lexer)?), + exprs = exprs, }); - if (!(try(lexer, ltok::COMMA) is lex::token)) { - want(lexer, ltok::RBRACE)?; + if (try(lexer, ltok::RBRACE)? is lex::token) { break; }; }; @@ -983,24 +1039,28 @@ fn match_case(lexer: *lex::lexer) (ast::match_case | error) = { let tok = peek(lexer)? as lex::token; let loc = tok.2; let nt = switch (tok.0) { - ltok::NULL => { - want(lexer, ltok::NULL)?; - yield ("", ast::_type { - start = loc, - end = lex::prevloc(lexer), - flags = 0, - repr = ast::builtin_type::NULL, - }); - }, - * => nametype(lexer)?, + case ltok::NULL => + want(lexer, ltok::NULL)?; + yield ("", ast::_type { + start = loc, + end = lex::prevloc(lexer), + flags = 0, + repr = ast::builtin_type::NULL, + }); + case => + yield nametype(lexer)?; + }; + want(lexer, ltok::ARROW)?; + let exprs: []*ast::expr = []; + for (peek(lexer, ltok::CASE, ltok::RBRACE)? is void) { + append(exprs, alloc(expression(lexer)?)); + want(lexer, ltok::SEMICOLON)?; }; - want(lexer, ltok::CASE)?; - let expr = expression(lexer)?; return ast::match_case { name = nt.0, _type = alloc(nt.1), - value = alloc(expr), + exprs = exprs, }; }; @@ -1012,41 +1072,26 @@ fn match_expr(lexer: *lex::lexer) (ast::expr | error) = { want(lexer, ltok::LBRACE)?; let cases: []ast::match_case = []; - let default: nullable *ast::expr = null; + let default: []*ast::expr = []; for (true) { - match (try(lexer, ltok::TIMES)?) { - void => append(cases, match_case(lexer)?), - t: lex::token => match (try(lexer, ltok::CASE)?) { - void => { - let case = match_case(lexer)?; - case._type = alloc(ast::_type { - start = t.2, - end = case._type.end, - flags = 0, - repr = ast::pointer_type { - referent = case._type, - flags = 0, - }, - }); - append(cases, case); - }, - lex::token => if (default == null) { - default = alloc(expression(lexer)?); - } else return syntaxerr(t.2, - "More than one default match case"), - }, - }; + want(lexer, ltok::CASE)?; - match (try(lexer, ltok::COMMA)?) { - void => { - want(lexer, ltok::RBRACE)?; - break; - }, - lex::token => void, + match (try(lexer, ltok::ARROW)?) { + case t: lex::token => + if (len(default) != 0) { + return syntaxerr(t.2, + "More than one default match case"); + }; + for (peek(lexer, ltok::CASE, ltok::RBRACE)? is void) { + append(default, alloc(expression(lexer)?)); + want(lexer, ltok::SEMICOLON)?; + }; + case void => + append(cases, match_case(lexer)?); }; - match (try(lexer, ltok::RBRACE)?) { - void => void, - lex::token => break, + + if (try(lexer, ltok::RBRACE)? is lex::token) { + break; }; }; @@ -1063,20 +1108,30 @@ fn match_expr(lexer: *lex::lexer) (ast::expr | error) = { fn unarithm(lexer: *lex::lexer) (ast::expr | error) = { const tok = match (try(lexer, - ltok::PLUS, ltok::MINUS, ltok::BNOT, - ltok::LNOT, ltok::TIMES, ltok::BAND)) { - void => return builtin(lexer), - tok: lex::token => tok, + ltok::PLUS, ltok::MINUS, ltok::BNOT, + ltok::LNOT, ltok::TIMES, ltok::BAND)) { + case void => + return builtin(lexer); + case tok: lex::token => + yield tok; }; + const op = switch (tok.0) { - ltok::PLUS => ast::unarithm_op::PLUS, - ltok::MINUS => ast::unarithm_op::MINUS, - ltok::BNOT => ast::unarithm_op::BNOT, - ltok::LNOT => ast::unarithm_op::LNOT, - ltok::TIMES => ast::unarithm_op::DEREF, - ltok::BAND => ast::unarithm_op::ADDR, - * => abort(), + case ltok::PLUS => + yield ast::unarithm_op::PLUS; + case ltok::MINUS => + yield ast::unarithm_op::MINUS; + case ltok::BNOT => + yield ast::unarithm_op::BNOT; + case ltok::LNOT => + yield ast::unarithm_op::LNOT; + case ltok::TIMES => + yield ast::unarithm_op::DEREF; + case ltok::BAND => + yield ast::unarithm_op::ADDR; + case => abort(); }; + let operand = unarithm(lexer)?; return ast::expr { start = tok.2, @@ -1093,15 +1148,20 @@ fn yield_expr(lexer: *lex::lexer) (ast::expr | error) = { let label = ""; let value: nullable *ast::expr = null; match (try(lexer, ltok::SEMICOLON, ltok::LABEL)?) { - void => value = alloc(expression(lexer)?), - t: lex::token => switch (t.0) { - ltok::SEMICOLON => lex::unlex(lexer, t), - ltok::LABEL => { - label = t.1 as str; - if (try(lexer, ltok::COMMA)? is void) yield; + case void => + value = alloc(expression(lexer)?); + case t: lex::token => + switch (t.0) { + case ltok::SEMICOLON => + lex::unlex(lexer, t); + case ltok::LABEL => + label = t.1 as str; + match (try(lexer, ltok::COMMA)?) { + case void => void; + case lex::token => value = alloc(expression(lexer)?); - }, - }, + }; + }; }; return ast::expr { start = start.2, @@ -1113,40 +1173,75 @@ fn yield_expr(lexer: *lex::lexer) (ast::expr | error) = { }; }; -fn binop_for_tok(tok: lex::token) ast::binarithm_op = switch (tok.0) { - ltok::BAND => ast::binarithm_op::BAND, - ltok::BOR => ast::binarithm_op::BOR, - ltok::BXOR => ast::binarithm_op::BXOR, - ltok::DIV => ast::binarithm_op::DIV, - ltok::GREATER => ast::binarithm_op::GT, - ltok::GREATEREQ => ast::binarithm_op::GTEQ, - ltok::LAND => ast::binarithm_op::LAND, - ltok::LEQUAL => ast::binarithm_op::LEQUAL, - ltok::LESS => ast::binarithm_op::LESS, - ltok::LESSEQ => ast::binarithm_op::LESSEQ, - ltok::LOR => ast::binarithm_op::LOR, - ltok::LSHIFT => ast::binarithm_op::LSHIFT, - ltok::LXOR => ast::binarithm_op::LXOR, - ltok::MINUS => ast::binarithm_op::MINUS, - ltok::MODULO => ast::binarithm_op::MODULO, - ltok::NEQUAL => ast::binarithm_op::NEQUAL, - ltok::PLUS => ast::binarithm_op::PLUS, - ltok::RSHIFT => ast::binarithm_op::RSHIFT, - ltok::TIMES => ast::binarithm_op::TIMES, - * => abort(), +fn binop_for_tok(tok: lex::token) ast::binarithm_op = { + switch (tok.0) { + case ltok::BAND => + return ast::binarithm_op::BAND; + case ltok::BOR => + return ast::binarithm_op::BOR; + case ltok::BXOR => + return ast::binarithm_op::BXOR; + case ltok::DIV => + return ast::binarithm_op::DIV; + case ltok::GREATER => + return ast::binarithm_op::GT; + case ltok::GREATEREQ => + return ast::binarithm_op::GTEQ; + case ltok::LAND => + return ast::binarithm_op::LAND; + case ltok::LEQUAL => + return ast::binarithm_op::LEQUAL; + case ltok::LESS => + return ast::binarithm_op::LESS; + case ltok::LESSEQ => + return ast::binarithm_op::LESSEQ; + case ltok::LOR => + return ast::binarithm_op::LOR; + case ltok::LSHIFT => + return ast::binarithm_op::LSHIFT; + case ltok::LXOR => + return ast::binarithm_op::LXOR; + case ltok::MINUS => + return ast::binarithm_op::MINUS; + case ltok::MODULO => + return ast::binarithm_op::MODULO; + case ltok::NEQUAL => + return ast::binarithm_op::NEQUAL; + case ltok::PLUS => + return ast::binarithm_op::PLUS; + case ltok::RSHIFT => + return ast::binarithm_op::RSHIFT; + case ltok::TIMES => + return ast::binarithm_op::TIMES; + case => abort(); + }; }; -fn precedence(tok: lex::token) int = switch (tok.0) { - ltok::LOR => 0, - ltok::LXOR => 1, - ltok::LAND => 2, - ltok::LEQUAL, ltok::NEQUAL => 3, - ltok::LESS, ltok::LESSEQ, ltok::GREATER, ltok::GREATEREQ => 4, - ltok::BOR => 5, - ltok::BXOR => 6, - ltok::BAND => 7, - ltok::LSHIFT, ltok::RSHIFT => 8, - ltok::PLUS, ltok::MINUS => 9, - ltok::TIMES, ltok::DIV, ltok::MODULO => 10, - * => -1, +fn precedence(tok: lex::token) int = { + switch (tok.0) { + case ltok::LOR => + return 0; + case ltok::LXOR => + return 1; + case ltok::LAND => + return 2; + case ltok::LEQUAL, ltok::NEQUAL => + return 3; + case ltok::LESS, ltok::LESSEQ, ltok::GREATER, ltok::GREATEREQ => + return 4; + case ltok::BOR => + return 5; + case ltok::BXOR => + return 6; + case ltok::BAND => + return 7; + case ltok::LSHIFT, ltok::RSHIFT => + return 8; + case ltok::PLUS, ltok::MINUS => + return 9; + case ltok::TIMES, ltok::DIV, ltok::MODULO => + return 10; + case => + return -1; + }; }; diff --git a/hare/parse/ident.ha b/hare/parse/ident.ha @@ -10,14 +10,16 @@ fn ident_trailing(lexer: *lex::lexer) ((ast::ident, bool) | error) = { let z = 0z; for (true) { let name = match (try(lexer, ltok::NAME)?) { - t: lex::token => t.1 as str, - void => return (ident: ast::ident, true), + case t: lex::token => + yield t.1 as str; + case void => + return (ident: ast::ident, true); }; append(ident, name); z += len(name); match (try(lexer, ltok::DOUBLE_COLON)?) { - void => break, - * => void, // Grab the next ident + case void => break; + case => void; // Grab the next ident }; z += 1; }; diff --git a/hare/parse/import.ha b/hare/parse/import.ha @@ -6,13 +6,17 @@ fn name_list(lexer: *lex::lexer) ([]str | error) = { let names: []str = []; for (true) { append(names, want(lexer, ltok::NAME)?.1 as str); + switch (want(lexer, ltok::COMMA, ltok::RBRACE)?.0) { - ltok::COMMA => match (try(lexer, ltok::RBRACE)?) { - void => void, - * => return names, - }, - ltok::RBRACE => return names, - * => abort(), // Unreachable + case ltok::COMMA => + match (try(lexer, ltok::RBRACE)?) { + case void => void; + case => + return names; + }; + case ltok::RBRACE => + return names; + case => abort(); // Unreachable }; }; abort(); @@ -23,41 +27,37 @@ export fn imports(lexer: *lex::lexer) ([]ast::import | error) = { let imports: []ast::import = []; for (true) { match (try(lexer, ltok::USE)?) { - void => break, - * => void, + case void => break; + case => void; }; let name = ident_trailing(lexer)?; - switch (want(lexer, ltok::SEMICOLON, ltok::LBRACE, - ltok::EQUAL)?.0) { - ltok::SEMICOLON => { - synassert(lex::mkloc(lexer), !name.1, - "Unexpected trailing :: in ident")?; - append(imports, name.0: ast::import_module); - }, - ltok::LBRACE => { - synassert(lex::mkloc(lexer), name.1, - "Expected trailing :: in ident")?; - let objects = name_list(lexer)?; - append(imports, ast::import_objects { - ident = name.0, - objects = objects, - }); - want(lexer, ltok::SEMICOLON)?; - }, - ltok::EQUAL => { - synassert(lex::mkloc(lexer), - len(name.0) == 1 && !name.1, - "Expected name, not ident")?; - let ident = ident(lexer)?; - append(imports, ast::import_alias { - ident = ident, - alias = name.0[0], - }); - want(lexer, ltok::SEMICOLON)?; - }, - * => abort(), // Unreachable + ltok::EQUAL)?.0) { + case ltok::SEMICOLON => + synassert(lex::mkloc(lexer), !name.1, + "Unexpected trailing :: in ident")?; + append(imports, name.0: ast::import_module); + case ltok::LBRACE => + synassert(lex::mkloc(lexer), name.1, + "Expected trailing :: in ident")?; + let objects = name_list(lexer)?; + append(imports, ast::import_objects { + ident = name.0, + objects = objects, + }); + want(lexer, ltok::SEMICOLON)?; + case ltok::EQUAL => + synassert(lex::mkloc(lexer), + len(name.0) == 1 && !name.1, + "Expected name, not ident")?; + let ident = ident(lexer)?; + append(imports, ast::import_alias { + ident = ident, + alias = name.0[0], + }); + want(lexer, ltok::SEMICOLON)?; + case => abort(); // Unreachable }; }; return imports; diff --git a/hare/parse/parse.ha b/hare/parse/parse.ha @@ -91,41 +91,40 @@ fn synassert(loc: lex::location, cond: bool, msg: str) (void | error) = { fn nametype(lexer: *lex::lexer) ((str, ast::_type) | error) = { let tok = peek(lexer)? as lex::token; const start = tok.2; - return switch (tok.0) { - ltok::NAME => { - let name = tok.1 as str; - want(lexer, ltok::NAME)?; - tok = peek(lexer)? as lex::token; - yield switch (tok.0) { - ltok::COLON => { - want(lexer, ltok::COLON)?; - yield (name, _type(lexer)?); + switch (tok.0) { + case ltok::NAME => + let name = tok.1 as str; + want(lexer, ltok::NAME)?; + tok = peek(lexer)? as lex::token; + switch (tok.0) { + case ltok::COLON => + want(lexer, ltok::COLON)?; + return (name, _type(lexer)?); + case ltok::DOUBLE_COLON => + want(lexer, ltok::DOUBLE_COLON)?; + let id = ident(lexer)?; + insert(id[0], name); + return ("", ast::_type { + start = start, + end = lex::prevloc(lexer), + flags = 0, + repr = ast::alias_type { + unwrap = false, + ident = id, }, - ltok::DOUBLE_COLON => { - want(lexer, ltok::DOUBLE_COLON)?; - let id = ident(lexer)?; - insert(id[0], name); - yield ("", ast::_type { - start = start, - end = lex::prevloc(lexer), - flags = 0, - repr = ast::alias_type { - unwrap = false, - ident = id, - }, - }); + }); + case => + return ("", ast::_type { + start = start, + end = lex::prevloc(lexer), + flags = 0, + repr = ast::alias_type { + unwrap = false, + ident = alloc([name]), }, - * => ("", ast::_type { - start = start, - end = lex::prevloc(lexer), - flags = 0, - repr = ast::alias_type { - unwrap = false, - ident = alloc([name]), - }, - }), - }; - }, - * => ("", _type(lexer)?), + }); + }; + case => + return ("", _type(lexer)?); }; }; diff --git a/hare/parse/type.ha b/hare/parse/type.ha @@ -10,20 +10,24 @@ fn prototype(lexer: *lex::lexer) (ast::func_type | error) = { for (try(lexer, ltok::RPAREN)? is void) { let loc = lex::mkloc(lexer); match (try(lexer, ltok::ELLIPSIS)?) { - void => void, - lex::token => { - synassert(loc, len(params) > 0, - "Expected at least one non-variadic parameter for C-style variadism")?; - variadism = ast::variadism::C; - try(lexer, ltok::COMMA)?; - want(lexer, ltok::RPAREN)?; - break; - }, + case void => + yield void; + case lex::token => + synassert(loc, len(params) > 0, + "Expected at least one non-variadic parameter for C-style variadism")?; + variadism = ast::variadism::C; + try(lexer, ltok::COMMA)?; + want(lexer, ltok::RPAREN)?; + break; }; + let name = match (try(lexer, ltok::UNDERSCORE)?) { - void => want(lexer, ltok::NAME)?.1 as str, - lex::token => "", + case void => + yield want(lexer, ltok::NAME)?.1 as str; + case lex::token => + yield ""; }; + want(lexer, ltok::COLON)?; append(params, ast::func_param { loc = loc, @@ -31,20 +35,19 @@ fn prototype(lexer: *lex::lexer) (ast::func_type | error) = { _type = alloc(_type(lexer)?), }); match (try(lexer, ltok::ELLIPSIS)?) { - void => void, - lex::token => { - variadism = ast::variadism::HARE; - try(lexer, ltok::COMMA)?; - want(lexer, ltok::RPAREN)?; - break; - }, + case void => + yield void; + case lex::token => + variadism = ast::variadism::HARE; + try(lexer, ltok::COMMA)?; + want(lexer, ltok::RPAREN)?; + break; }; match (try(lexer, ltok::COMMA)?) { - void => { - want(lexer, ltok::RPAREN)?; - break; - }, - lex::token => void, + case void => + want(lexer, ltok::RPAREN)?; + break; + case lex::token => void; }; }; let t = _type(lexer)?; @@ -58,44 +61,67 @@ fn prototype(lexer: *lex::lexer) (ast::func_type | error) = { fn integer_type( lexer: *lex::lexer, -) (builtin_type | error) = switch (want(lexer)?.0) { - ltok::CHAR => builtin_type::CHAR, - ltok::I16 => builtin_type::I16, - ltok::I32 => builtin_type::I32, - ltok::I64 => builtin_type::I64, - ltok::I64 => builtin_type::I64, - ltok::I8 => builtin_type::I8, - ltok::INT => builtin_type::INT, - ltok::SIZE => builtin_type::SIZE, - ltok::U16 => builtin_type::U16, - ltok::U32 => builtin_type::U32, - ltok::U64 => builtin_type::U64, - ltok::U64 => builtin_type::U64, - ltok::U8 => builtin_type::U8, - ltok::UINT => builtin_type::UINT, - ltok::UINTPTR => builtin_type::UINTPTR, - * => syntaxerr(lex::mkloc(lexer), "Expected integer type"), +) (builtin_type | error) = { + switch (want(lexer)?.0) { + case ltok::CHAR => + return builtin_type::CHAR; + case ltok::I16 => + return builtin_type::I16; + case ltok::I32 => + return builtin_type::I32; + case ltok::I64 => + return builtin_type::I64; + case ltok::I64 => + return builtin_type::I64; + case ltok::I8 => + return builtin_type::I8; + case ltok::INT => + return builtin_type::INT; + case ltok::SIZE => + return builtin_type::SIZE; + case ltok::U16 => + return builtin_type::U16; + case ltok::U32 => + return builtin_type::U32; + case ltok::U64 => + return builtin_type::U64; + case ltok::U64 => + return builtin_type::U64; + case ltok::U8 => + return builtin_type::U8; + case ltok::UINT => + return builtin_type::UINT; + case ltok::UINTPTR => + return builtin_type::UINTPTR; + case => + return syntaxerr(lex::mkloc(lexer), "Expected integer type"); + }; }; fn primitive_type(lexer: *lex::lexer) (ast::_type | error) = { let tok = want(lexer)?; let builtin = switch (tok.0) { - ltok::CHAR, ltok::I16, ltok::I32, ltok::I64, - ltok::I64, ltok::I8, ltok::INT, ltok::SIZE, ltok::U16, - ltok::U32, ltok::U64, ltok::U64, ltok::U8, ltok::UINT, - ltok::UINTPTR => { - lex::unlex(lexer, tok); - yield integer_type(lexer)?; - }, - ltok::RUNE => builtin_type::RUNE, - ltok::STR => builtin_type::STR, - ltok::F32 => builtin_type::F32, - ltok::F64 => builtin_type::F64, - ltok::BOOL => builtin_type::BOOL, - ltok::VOID => builtin_type::VOID, - * => return syntaxerr(lex::mkloc(lexer), + case ltok::CHAR, ltok::I16, ltok::I32, ltok::I64, ltok::I64, ltok::I8, + ltok::INT, ltok::SIZE, ltok::U16, ltok::U32, ltok::U64, + ltok::U64, ltok::U8, ltok::UINT, ltok::UINTPTR => + lex::unlex(lexer, tok); + yield integer_type(lexer)?; + case ltok::RUNE => + yield builtin_type::RUNE; + case ltok::STR => + yield builtin_type::STR; + case ltok::F32 => + yield builtin_type::F32; + case ltok::F64 => + yield builtin_type::F64; + case ltok::BOOL => + yield builtin_type::BOOL; + case ltok::VOID => + yield builtin_type::VOID; + case => + return syntaxerr(lex::mkloc(lexer), "Unexected {}, was expecting primitive type", - lex::tokstr(tok)), + lex::tokstr(tok)); }; return ast::_type { start = tok.2, @@ -107,10 +133,7 @@ fn primitive_type(lexer: *lex::lexer) (ast::_type | error) = { fn alias_type(lexer: *lex::lexer) (ast::_type | error) = { const start = lex::mkloc(lexer); - let unwrap = match (try(lexer, ltok::ELLIPSIS)?) { - void => false, - * => true, - }; + let unwrap = try(lexer, ltok::ELLIPSIS)? is lex::token; let ident = ident(lexer)?; return ast::_type { start = start, @@ -126,8 +149,10 @@ fn alias_type(lexer: *lex::lexer) (ast::_type | error) = { fn pointer_type(lexer: *lex::lexer) (ast::_type | error) = { const start = lex::mkloc(lexer); let flags = match (try(lexer, ltok::NULLABLE)?) { - void => 0: ast::pointer_flags, - * => ast::pointer_flags::NULLABLE, + case void => + yield 0: ast::pointer_flags; + case => + yield ast::pointer_flags::NULLABLE; }; want(lexer, ltok::TIMES)?; let _type = _type(lexer)?; @@ -151,12 +176,9 @@ fn tagged_type( append(tagged, alloc(first)); for (try(lexer, ltok::RPAREN)? is void) { append(tagged, alloc(_type(lexer)?)); - match (try(lexer, ltok::BOR)?) { - void => { - want(lexer, ltok::RPAREN)?; - break; - }, - lex::token => void, + if (try(lexer, ltok::BOR)? is void) { + want(lexer, ltok::RPAREN)?; + break; }; }; return ast::_type { @@ -176,12 +198,9 @@ fn tuple_type( append(tuple, alloc(first)); for (try(lexer, ltok::RPAREN)? is void) { append(tuple, alloc(_type(lexer)?)); - match (try(lexer, ltok::COMMA)?) { - void => { - want(lexer, ltok::RPAREN)?; - break; - }, - lex::token => void, + if (try(lexer, ltok::COMMA)? is void) { + want(lexer, ltok::RPAREN)?; + break; }; }; return ast::_type { @@ -195,8 +214,10 @@ fn tuple_type( fn fn_type(lexer: *lex::lexer) (ast::_type | error) = { const start = lex::mkloc(lexer); let attrs = match (try(lexer, ltok::ATTR_NORETURN)?) { - void => 0: ast::func_attrs, - * => ast::func_attrs::NORETURN, + case void => + yield 0: ast::func_attrs; + case => + yield ast::func_attrs::NORETURN; }; want(lexer, ltok::FN)?; let proto = prototype(lexer)?; @@ -222,37 +243,35 @@ fn struct_union_type(lexer: *lex::lexer) (ast::_type | error) = { }; let offs: nullable *ast::expr = match (try(lexer, ltok::ATTR_OFFSET)?) { - void => null, - lex::token => { - want(lexer, ltok::LPAREN)?; - let ex = expression(lexer)?; - want(lexer, ltok::RPAREN)?; - yield alloc(ex); - }, + case void => + yield null; + case lex::token => + want(lexer, ltok::LPAREN)?; + let ex = expression(lexer)?; + want(lexer, ltok::RPAREN)?; + yield alloc(ex); }; let tok = want(lexer, ltok::NAME, ltok::STRUCT, ltok::UNION)?; switch (tok.0) { - ltok::NAME => { - lex::unlex(lexer, tok); - let memb = struct_embed_or_field(lexer, offs)?; - append(membs, memb); - }, - ltok::STRUCT, ltok::UNION => { - lex::unlex(lexer, tok); - let subtype = struct_union_type(lexer)?; - append(membs, ast::struct_member { - _offset = offs, - member = alloc(subtype), - }); - }, - * => abort(), + case ltok::NAME => + lex::unlex(lexer, tok); + let memb = struct_embed_or_field(lexer, offs)?; + append(membs, memb); + case ltok::STRUCT, ltok::UNION => + lex::unlex(lexer, tok); + let subtype = struct_union_type(lexer)?; + append(membs, ast::struct_member { + _offset = offs, + member = alloc(subtype), + }); + case => abort(); }; - switch (want(lexer, ltok::COMMA, ltok::RBRACE)?.0) { - ltok::COMMA => void, - ltok::RBRACE => break, - * => abort(), + switch (want(lexer, ltok::RBRACE, ltok::COMMA)?.0) { + case ltok::RBRACE => break; + case ltok::COMMA => void; + case => abort(); }; }; @@ -261,9 +280,11 @@ fn struct_union_type(lexer: *lex::lexer) (ast::_type | error) = { end = lex::prevloc(lexer), flags = 0, repr = switch (kind.0) { - ltok::STRUCT => membs: ast::struct_type, - ltok::UNION => membs: ast::union_type, - * => abort(), + case ltok::STRUCT => + yield membs: ast::struct_type; + case ltok::UNION => + yield membs: ast::union_type; + case => abort(); }, }; }; @@ -284,25 +305,25 @@ fn struct_embed_or_field( let name = want(lexer, ltok::NAME)?; let id: ast::ident = match (try(lexer, ltok::COLON, ltok::DOUBLE_COLON)?) { - void => alloc([name.1 as str]), - tok: lex::token => switch (tok.0) { - ltok::COLON => { - let field = ast::struct_field { - name = name.1 as str, - _type = alloc(_type(lexer)?), - }; - return ast::struct_member { - _offset = offs, - member = field, - }; - }, - ltok::DOUBLE_COLON => { - let id = ident(lexer)?; - insert(id[0], name.1 as str); - yield id; - }, - * => abort(), - }, + case void => + yield alloc([name.1 as str]); + case tok: lex::token => + yield switch (tok.0) { + case ltok::COLON => + let field = ast::struct_field { + name = name.1 as str, + _type = alloc(_type(lexer)?), + }; + return ast::struct_member { + _offset = offs, + member = field, + }; + case ltok::DOUBLE_COLON => + let id = ident(lexer)?; + insert(id[0], name.1 as str); + yield id; + case => abort(); + }; }; return ast::struct_member { @@ -314,21 +335,26 @@ fn struct_embed_or_field( fn array_slice_type(lexer: *lex::lexer) (ast::_type | error) = { let start = want(lexer, ltok::LBRACKET)?; - let length = match (try(lexer, - ltok::UNDERSCORE, ltok::TIMES, ltok::RBRACKET)?) { - void => alloc(expression(lexer)?), - tok: lex::token => switch (tok.0) { - ltok::UNDERSCORE => ast::len_contextual, - ltok::TIMES => ast::len_unbounded, - ltok::RBRACKET => ast::len_slice, - * => abort(), - }, + let length = match (try(lexer, ltok::UNDERSCORE, + ltok::TIMES, ltok::RBRACKET)?) { + case void => + yield alloc(expression(lexer)?); + case tok: lex::token => + yield switch (tok.0) { + case ltok::UNDERSCORE => + yield ast::len_contextual; + case ltok::TIMES => + yield ast::len_unbounded; + case ltok::RBRACKET => + yield ast::len_slice; + case => abort(); + }; }; - match (length) { - ast::len_slice => void, - * => want(lexer, ltok::RBRACKET)?, + if (!(length is ast::len_slice)) { + want(lexer, ltok::RBRACKET)?; }; + let _type = _type(lexer)?; return ast::_type { start = start.2, @@ -345,12 +371,12 @@ fn enum_type(lexer: *lex::lexer) (ast::_type | error) = { let start = want(lexer, ltok::ENUM)?; const storage = match (try(lexer, ltok::LBRACE)?) { - void => { - let storage = integer_type(lexer)?; - want(lexer, ltok::LBRACE)?; - yield storage; - }, - lex::token => builtin_type::INT, + case void => + let storage = integer_type(lexer)?; + want(lexer, ltok::LBRACE)?; + yield storage; + case lex::token => + yield builtin_type::INT; }; let membs: []ast::enum_field = []; @@ -373,9 +399,9 @@ fn enum_type(lexer: *lex::lexer) (ast::_type | error) = { }); switch (want(lexer, ltok::COMMA, ltok::RBRACE)?.0) { - ltok::COMMA => void, - ltok::RBRACE => break, - * => abort(), + case ltok::COMMA => void; + case ltok::RBRACE => break; + case => abort(); }; }; @@ -393,41 +419,48 @@ fn enum_type(lexer: *lex::lexer) (ast::_type | error) = { // Parses a type, e.g. '[]int'. export fn _type(lexer: *lex::lexer) (ast::_type | error) = { let flags: ast::type_flags = 0; - match (try(lexer, ltok::CONST)?) { - void => void, - * => flags |= ast::type_flags::CONST, + if (try(lexer, ltok::CONST)? is lex::token) { + flags |= ast::type_flags::CONST; }; - match (try(lexer, ltok::LNOT)?) { - void => void, - * => flags |= ast::type_flags::ERROR, + + if (try(lexer, ltok::LNOT)? is lex::token) { + flags |= ast::type_flags::ERROR; }; + let tok = peek(lexer)? as lex::token; let typ: ast::_type = switch (tok.0) { - ltok::CHAR, ltok::I16, ltok::I32, ltok::I64, - ltok::I64, ltok::I8, ltok::INT, ltok::SIZE, - ltok::U16, ltok::U32, ltok::U64, ltok::U64, - ltok::U8, ltok::UINT, ltok::UINTPTR, ltok::RUNE, - ltok::STR, ltok::F32, ltok::F64, ltok::BOOL, - ltok::VOID => primitive_type(lexer)?, - ltok::ENUM => enum_type(lexer)?, - ltok::NULLABLE, ltok::TIMES => pointer_type(lexer)?, - ltok::STRUCT, ltok::UNION => struct_union_type(lexer)?, - ltok::LBRACKET => array_slice_type(lexer)?, - ltok::LPAREN => { - want(lexer, ltok::LPAREN)?; - let t = _type(lexer)?; - yield switch (want(lexer, ltok::BOR, - ltok::COMMA)?.0) { - ltok::BOR => tagged_type(lexer, t, tok.2)?, - ltok::COMMA => tuple_type(lexer, t, tok.2)?, - * => abort("unreachable"), - }; - }, - ltok::ATTR_NORETURN, ltok::FN => fn_type(lexer)?, - ltok::ELLIPSIS, ltok::NAME => alias_type(lexer)?, - * => return syntaxerr(lex::mkloc(lexer), + case ltok::CHAR, ltok::I16, ltok::I32, ltok::I64, ltok::I64, ltok::I8, + ltok::INT, ltok::SIZE, ltok::U16, ltok::U32, ltok::U64, + ltok::U8, ltok::UINT, ltok::UINTPTR, ltok::RUNE, + ltok::STR, ltok::F32, ltok::F64, ltok::BOOL, + ltok::VOID => + yield primitive_type(lexer)?; + case ltok::ENUM => + yield enum_type(lexer)?; + case ltok::NULLABLE, ltok::TIMES => + yield pointer_type(lexer)?; + case ltok::STRUCT, ltok::UNION => + yield struct_union_type(lexer)?; + case ltok::LBRACKET => + yield array_slice_type(lexer)?; + case ltok::LPAREN => + want(lexer, ltok::LPAREN)?; + let t = _type(lexer)?; + yield switch (want(lexer, ltok::BOR, ltok::COMMA)?.0) { + case ltok::BOR => + yield tagged_type(lexer, t, tok.2)?; + case ltok::COMMA => + yield tuple_type(lexer, t, tok.2)?; + case => abort("unreachable"); + }; + case ltok::ATTR_NORETURN, ltok::FN => + yield fn_type(lexer)?; + case ltok::ELLIPSIS, ltok::NAME => + yield alias_type(lexer)?; + case => + return syntaxerr(lex::mkloc(lexer), "Unexpected {}, was expecting type", - lex::tokstr(tok)), + lex::tokstr(tok)); }; typ.flags |= flags; diff --git a/hare/types/class.ha b/hare/types/class.ha @@ -1,49 +1,67 @@ // Returns true if the given type is a signed type. export fn is_signed(ty: const *_type) bool = { - return match (ty.repr) { + match (ty.repr) { + case al: alias => // TODO: al.secondary as *_type - al: alias => is_signed(al.secondary: const *_type), - bi: builtin => switch (bi) { - builtin::F32, builtin::F64, - builtin::I16, builtin::I32, builtin::I64, builtin::I8, - builtin::INT => true, - * => false, - }, - e: _enum => switch (e.storage) { - builtin::I16, builtin::I32, builtin::I64, builtin::I8, - builtin::INT => true, - * => false, - }, - * => false, + return is_signed(al.secondary: const *_type); + case bi: builtin => + switch (bi) { + case builtin::F32, builtin::F64, builtin::I16, builtin::I32, + builtin::I64, builtin::I8, builtin::INT => + return true; + case => + return false; + }; + case e: _enum => + switch (e.storage) { + case builtin::I16, builtin::I32, builtin::I64, builtin::I8, + builtin::INT => + return true; + case => + return false; + }; + case => + return false; }; }; // Returns true if the given type is a floating-point type. export fn is_float(ty: const *_type) bool = { - return match (ty.repr) { + match (ty.repr) { + case al: alias => // TODO: al.secondary as *_type - al: alias => is_float(al.secondary: const *_type), - bi: builtin => switch (bi) { - builtin::F32, builtin::F64 => true, - * => false, - }, - * => false, + return is_float(al.secondary: const *_type); + case bi: builtin => + switch (bi) { + case builtin::F32, builtin::F64 => + return true; + case => + return false; + }; + case => + return false; }; }; // Returns true if the given type is an integer type. export fn is_integer(ty: const *_type) bool = { - return match (ty.repr) { + match (ty.repr) { + case al: alias => // TODO: al.secondary as *_type - al: alias => is_integer(al.secondary: const *_type), - bi: builtin => switch (bi) { - builtin::INT, builtin::UINT, - builtin::I16, builtin::I32, builtin::I64, builtin::I8, - builtin::U16, builtin::U32, builtin::U64, builtin::U8, - builtin::CHAR, builtin::SIZE, builtin::UINTPTR => true, - * => false, - }, - _enum => true, - * => false, + return is_integer(al.secondary: const *_type); + case bi: builtin => + switch (bi) { + case builtin::INT, builtin::UINT, builtin::I16, + builtin::I32, builtin::I64, builtin::I8, builtin::U16, + builtin::U32, builtin::U64, builtin::U8, builtin::CHAR, + builtin::SIZE, builtin::UINTPTR => + return true; + case => + return false; + }; + case _enum => + return true; + case => + return false; }; }; diff --git a/hare/types/hash.ha b/hare/types/hash.ha @@ -11,43 +11,78 @@ type storage = enum u8 { FUNCTION, POINTER, SLICE, STRING, STRUCT, TAGGED, TUPLE, UNION, }; -fn builtin_storage(b: builtin) u8 = switch (b) { - builtin::BOOL => storage::BOOL, - builtin::CHAR => storage::CHAR, - builtin::F32 => storage::F32, - builtin::F64 => storage::F64, - builtin::I16 => storage::I16, - builtin::I32 => storage::I32, - builtin::I64 => storage::I64, - builtin::I8 => storage::I8, - builtin::INT => storage::INT, - builtin::NULL => storage::NULL, - builtin::RUNE => storage::RUNE, - builtin::SIZE => storage::SIZE, - builtin::STR => storage::STRING, - builtin::U16 => storage::U16, - builtin::U32 => storage::U32, - builtin::U64 => storage::U64, - builtin::U8 => storage::U8, - builtin::UINT => storage::UINT, - builtin::UINTPTR => storage::UINTPTR, - builtin::VOID => storage::VOID, +fn builtin_storage(b: builtin) u8 = { + switch (b) { + case builtin::BOOL => + return storage::BOOL; + case builtin::CHAR => + return storage::CHAR; + case builtin::F32 => + return storage::F32; + case builtin::F64 => + return storage::F64; + case builtin::I16 => + return storage::I16; + case builtin::I32 => + return storage::I32; + case builtin::I64 => + return storage::I64; + case builtin::I8 => + return storage::I8; + case builtin::INT => + return storage::INT; + case builtin::NULL => + return storage::NULL; + case builtin::RUNE => + return storage::RUNE; + case builtin::SIZE => + return storage::SIZE; + case builtin::STR => + return storage::STRING; + case builtin::U16 => + return storage::U16; + case builtin::U32 => + return storage::U32; + case builtin::U64 => + return storage::U64; + case builtin::U8 => + return storage::U8; + case builtin::UINT => + return storage::UINT; + case builtin::UINTPTR => + return storage::UINTPTR; + case builtin::VOID => + return storage::VOID; + }; }; -fn type_storage(t: *_type) u8 = match (t.repr) { - alias => storage::ALIAS, - array => storage::ARRAY, - b: builtin => builtin_storage(b), - _enum => storage::ENUM, - func => storage::FUNCTION, - pointer => storage::POINTER, - slice => storage::SLICE, - st: _struct => - if (st.kind == struct_union::STRUCT) - storage::STRUCT - else storage::UNION, - tuple => storage::TUPLE, - tagged => storage::TAGGED, +fn type_storage(t: *_type) u8 = { + match (t.repr) { + case alias => + return storage::ALIAS; + case array => + return storage::ARRAY; + case b: builtin => + return builtin_storage(b); + case _enum => + return storage::ENUM; + case func => + return storage::FUNCTION; + case pointer => + return storage::POINTER; + case slice => + return storage::SLICE; + case st: _struct => + if (st.kind == struct_union::STRUCT) { + return storage::STRUCT; + } else { + return storage::UNION; + }; + case tuple => + return storage::TUPLE; + case tagged => + return storage::TAGGED; + }; }; fn write8(h: *hash::hash, u: u8) void = { @@ -77,49 +112,50 @@ export fn hash(t: *_type) u32 = { write8(&id, t.flags); match (t.repr) { - a: alias => for (let i = len(a.id); i > 0; i -= 1) { + case a: alias => + for (let i = len(a.id); i > 0; i -= 1) { hash::write(&id, strings::toutf8(a.id[i - 1])); write8(&id, 0); - }, - a: array => { - write32(&id, hash(a.member)); - static assert(size(u64) == size(size)); // TODO - write64(&id, a.length); - }, - builtin => void, - e: _enum => { - write8(&id, builtin_storage(e.storage)); - for (let i = 0z; i < len(e.values); i += 1) { - hash::write(&id, strings::toutf8(e.values[i].0)); - write64(&id, e.values[i].1); - }; - }, - f: func => { - write32(&id, hash(f.result)); - write8(&id, f.variadism: u8); - write8(&id, f.flags: u8); - for (let i = 0z; i < len(f.params); i += 1) { - write32(&id, hash(f.params[i])); - }; - }, - p: pointer => { - write8(&id, p.flags); - write32(&id, hash(p.referent)); - }, - s: slice => write32(&id, hash(s)), - st: _struct => for (let i = 0z; i < len(st.fields); i += 1) { + }; + case a: array => + write32(&id, hash(a.member)); + static assert(size(u64) == size(size)); // TODO + write64(&id, a.length); + case builtin => void; + case e: _enum => + write8(&id, builtin_storage(e.storage)); + for (let i = 0z; i < len(e.values); i += 1) { + hash::write(&id, strings::toutf8(e.values[i].0)); + write64(&id, e.values[i].1); + }; + case f: func => + write32(&id, hash(f.result)); + write8(&id, f.variadism: u8); + write8(&id, f.flags: u8); + for (let i = 0z; i < len(f.params); i += 1) { + write32(&id, hash(f.params[i])); + }; + case p: pointer => + write8(&id, p.flags); + write32(&id, hash(p.referent)); + case s: slice => + write32(&id, hash(s)); + case st: _struct => + for (let i = 0z; i < len(st.fields); i += 1) { const field = st.fields[i]; hash::write(&id, strings::toutf8(field.name)); write32(&id, hash(field._type)); static assert(size(u64) == size(size)); // TODO write64(&id, field.offs); - }, - tu: tuple => for (let i = 0z; i < len(tu); i += 1) { + }; + case tu: tuple => + for (let i = 0z; i < len(tu); i += 1) { write32(&id, hash(tu[i]._type)); - }, - ta: tagged => for (let i = 0z; i < len(ta); i += 1) { + }; + case ta: tagged => + for (let i = 0z; i < len(ta); i += 1) { write32(&id, hash(ta[i])); - }, + }; }; return fnv::sum32(&id); diff --git a/hare/types/lookup.ha b/hare/types/lookup.ha @@ -3,11 +3,11 @@ use hare::ast; // Unwraps a type which may be aliased and returns the underlying type. export fn dealias(t: *_type) const *_type = { for (true) match (t.repr) { - a: alias => { - assert(a.secondary != null); - t = a.secondary: const *_type; - }, - * => break, + case a: alias => + assert(a.secondary != null); + t = a.secondary: const *_type; + case => + break; }; return t; }; diff --git a/hare/types/store.ha b/hare/types/store.ha @@ -69,23 +69,37 @@ export fn lookup( ) (const *_type | deferred | error) = { const ty = fromast(store, ty)?; if (ty.flags == 0) match (ty.repr) { - b: builtin => switch (b) { - builtin::CHAR => return &builtin_char, - builtin::F32 => return &builtin_f32, - builtin::F64 => return &builtin_f64, - builtin::I8 => return &builtin_i8, - builtin::I16 => return &builtin_i16, - builtin::I32 => return &builtin_i32, - builtin::I64 => return &builtin_i64, - builtin::RUNE => return &builtin_rune, - builtin::U8 => return &builtin_u8, - builtin::U16 => return &builtin_u16, - builtin::U32 => return &builtin_u32, - builtin::U64 => return &builtin_u64, - builtin::VOID => return &builtin_void, - * => void, - }, - * => void, + case b: builtin => + switch (b) { + case builtin::CHAR => + return &builtin_char; + case builtin::F32 => + return &builtin_f32; + case builtin::F64 => + return &builtin_f64; + case builtin::I8 => + return &builtin_i8; + case builtin::I16 => + return &builtin_i16; + case builtin::I32 => + return &builtin_i32; + case builtin::I64 => + return &builtin_i64; + case builtin::RUNE => + return &builtin_rune; + case builtin::U8 => + return &builtin_u8; + case builtin::U16 => + return &builtin_u16; + case builtin::U32 => + return &builtin_u32; + case builtin::U64 => + return &builtin_u64; + case builtin::VOID => + return &builtin_void; + case => void; + }; + case => void; }; const id = hash(&ty); @@ -104,186 +118,162 @@ export fn lookup( fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = { let sz = SIZE_UNDEFINED, align = SIZE_UNDEFINED; const repr = match (atype.repr) { - a: ast::alias_type => { - // TODO: This is incomplete - assert(!a.unwrap); - yield alias { - id = ast::ident_dup(a.ident), - secondary = null, - }; - }, + case a: ast::alias_type => + // TODO: This is incomplete + assert(!a.unwrap); + yield alias { + id = ast::ident_dup(a.ident), + secondary = null, + }; + case b: ast::builtin_type => // TODO: Tuple unpacking could improve this - b: ast::builtin_type => switch (b) { - ast::builtin_type::BOOL => { - sz = store.arch._int; - align = store.arch._int; - yield builtin::BOOL; - }, - ast::builtin_type::CHAR => { - sz = 1; align = 1; - yield builtin::CHAR; - }, - ast::builtin_type::F32 => { - sz = 4; align = 4; - yield builtin::F32; - }, - ast::builtin_type::F64 => { - sz = 8; align = 8; - yield builtin::F64; - }, - ast::builtin_type::I16 => { - sz = 2; align = 2; - yield builtin::I16; - }, - ast::builtin_type::I32 => { - sz = 4; align = 4; - yield builtin::I32; - }, - ast::builtin_type::I64 => { - sz = 8; align = 8; - yield builtin::I64; - }, - ast::builtin_type::I8 => { - sz = 1; align = 1; - yield builtin::I8; - }, - ast::builtin_type::INT => { - sz = store.arch._int; - align = store.arch._int; - yield builtin::INT; - }, - ast::builtin_type::RUNE => { - sz = 4; align = 4; - yield builtin::RUNE; - }, - ast::builtin_type::SIZE => { - sz = store.arch._size; - align = store.arch._size; - yield builtin::SIZE; - }, - ast::builtin_type::STR => { - sz = store.arch._pointer; - sz += sz % store.arch._size + store.arch._size; - sz += store.arch._size; - align = if (store.arch._size > store.arch._pointer) - store.arch._size - else - store.arch._pointer; - yield builtin::STR; - }, - ast::builtin_type::U16 => { - sz = 2; align = 2; - yield builtin::U16; - }, - ast::builtin_type::U32 => { - sz = 4; align = 4; - yield builtin::U32; - }, - ast::builtin_type::U64 => { - sz = 8; align = 8; - yield builtin::U64; - }, - ast::builtin_type::U8 => { - sz = 1; align = 1; - yield builtin::U8; - }, - ast::builtin_type::UINT => { - sz = store.arch._int; - align = store.arch._int; - yield builtin::UINT; - }, - ast::builtin_type::UINTPTR => { - sz = store.arch._pointer; - align = store.arch._pointer; - yield builtin::UINTPTR; - }, - ast::builtin_type::VOID => { - sz = 0; align = 0; - yield builtin::VOID; - }, - ast::builtin_type::NULL => { - sz = store.arch._pointer; - align = store.arch._pointer; - yield builtin::NULL; - }, - ast::builtin_type::ICONST, - ast::builtin_type::FCONST => abort(), // TODO? - }, - f: ast::func_type => func_from_ast(store, &f)?, - p: ast::pointer_type => { + yield switch (b) { + case ast::builtin_type::BOOL => + sz = store.arch._int; + align = store.arch._int; + yield builtin::BOOL; + case ast::builtin_type::CHAR => + sz = 1; align = 1; + yield builtin::CHAR; + case ast::builtin_type::F32 => + sz = 4; align = 4; + yield builtin::F32; + case ast::builtin_type::F64 => + sz = 8; align = 8; + yield builtin::F64; + case ast::builtin_type::I16 => + sz = 2; align = 2; + yield builtin::I16; + case ast::builtin_type::I32 => + sz = 4; align = 4; + yield builtin::I32; + case ast::builtin_type::I64 => + sz = 8; align = 8; + yield builtin::I64; + case ast::builtin_type::I8 => + sz = 1; align = 1; + yield builtin::I8; + case ast::builtin_type::INT => + sz = store.arch._int; + align = store.arch._int; + yield builtin::INT; + case ast::builtin_type::RUNE => + sz = 4; align = 4; + yield builtin::RUNE; + case ast::builtin_type::SIZE => + sz = store.arch._size; + align = store.arch._size; + yield builtin::SIZE; + case ast::builtin_type::STR => + sz = store.arch._pointer; + sz += sz % store.arch._size + store.arch._size; + sz += store.arch._size; + align = if (store.arch._size > store.arch._pointer) + store.arch._size + else + store.arch._pointer; + yield builtin::STR; + case ast::builtin_type::U16 => + sz = 2; align = 2; + yield builtin::U16; + case ast::builtin_type::U32 => + sz = 4; align = 4; + yield builtin::U32; + case ast::builtin_type::U64 => + sz = 8; align = 8; + yield builtin::U64; + case ast::builtin_type::U8 => + sz = 1; align = 1; + yield builtin::U8; + case ast::builtin_type::UINT => + sz = store.arch._int; + align = store.arch._int; + yield builtin::UINT; + case ast::builtin_type::UINTPTR => sz = store.arch._pointer; align = store.arch._pointer; - yield pointer { - referent = lookup(store, p.referent)?, - flags = p.flags: pointer_flags, - }; - }, - st: ast::struct_type => { - let st = struct_from_ast(store, st, false)?; + yield builtin::UINTPTR; + case ast::builtin_type::VOID => sz = 0; align = 0; - for (let i = 0z; i < len(st.fields); i += 1) { - const field = st.fields[i]; - if (field.offs + field._type.sz > sz) { - sz = field.offs + field._type.sz; - }; - if (field._type.align > align) { - align = field._type.align; - }; + yield builtin::VOID; + case ast::builtin_type::NULL => + sz = store.arch._pointer; + align = store.arch._pointer; + yield builtin::NULL; + case ast::builtin_type::ICONST, ast::builtin_type::FCONST => + abort(); // TODO? + }; + case f: ast::func_type => + yield func_from_ast(store, &f)?; + case p: ast::pointer_type => + sz = store.arch._pointer; + align = store.arch._pointer; + yield pointer { + referent = lookup(store, p.referent)?, + flags = p.flags: pointer_flags, + }; + case st: ast::struct_type => + let st = struct_from_ast(store, st, false)?; + sz = 0; align = 0; + for (let i = 0z; i < len(st.fields); i += 1) { + const field = st.fields[i]; + if (field.offs + field._type.sz > sz) { + sz = field.offs + field._type.sz; }; - yield st; - }, - un: ast::union_type => { - let st = struct_from_ast(store, un, true)?; - sz = 0; align = 0; - for (let i = 0z; i < len(st.fields); i += 1) { - const field = st.fields[i]; - if (field.offs + field._type.sz > sz) { - sz = field.offs + field._type.sz; - }; - if (field._type.align > align) { - align = field._type.align; - }; + if (field._type.align > align) { + align = field._type.align; }; - yield st; - }, - ta: ast::tagged_type => { - let ta = tagged_from_ast(store, ta)?; - sz = 0; align = 0; - for (let i = 0z; i < len(ta); i += 1) { - if (ta[i].sz > sz) { - sz = ta[i].sz; - }; - if (ta[i].align > align) { - align = ta[i].align; - }; + }; + yield st; + case un: ast::union_type => + let st = struct_from_ast(store, un, true)?; + sz = 0; align = 0; + for (let i = 0z; i < len(st.fields); i += 1) { + const field = st.fields[i]; + if (field.offs + field._type.sz > sz) { + sz = field.offs + field._type.sz; }; - if (store.arch._int > align) { - align = store.arch._int; + if (field._type.align > align) { + align = field._type.align; }; - sz += store.arch._int % align + store.arch._int; - yield ta; - }, - tu: ast::tuple_type => { - let tu = tuple_from_ast(store, tu)?; - sz = 0; align = 0; - for (let i = 0z; i < len(tu); i += 1) { - const value = tu[i]; - if (value.offs + value._type.sz > sz) { - sz = value.offs + value._type.sz; - }; - if (value._type.align > align) { - align = value._type.align; - }; + }; + yield st; + case ta: ast::tagged_type => + let ta = tagged_from_ast(store, ta)?; + sz = 0; align = 0; + for (let i = 0z; i < len(ta); i += 1) { + if (ta[i].sz > sz) { + sz = ta[i].sz; }; - yield tu; - }, - lt: ast::list_type => { - let r = list_from_ast(store, &lt)?; - sz = r.0; - align = r.1; - yield r.2; - }, - et: ast::enum_type => abort(), // TODO + if (ta[i].align > align) { + align = ta[i].align; + }; + }; + if (store.arch._int > align) { + align = store.arch._int; + }; + sz += store.arch._int % align + store.arch._int; + yield ta; + case tu: ast::tuple_type => + let tu = tuple_from_ast(store, tu)?; + sz = 0; align = 0; + for (let i = 0z; i < len(tu); i += 1) { + const value = tu[i]; + if (value.offs + value._type.sz > sz) { + sz = value.offs + value._type.sz; + }; + if (value._type.align > align) { + align = value._type.align; + }; + }; + yield tu; + case lt: ast::list_type => + let r = list_from_ast(store, &lt)?; + sz = r.0; + align = r.1; + yield r.2; + case et: ast::enum_type => + abort(); // TODO }; if (sz != SIZE_UNDEFINED && sz != 0 && sz % align != 0) { sz += align - (sz - align) % align; @@ -303,9 +293,12 @@ fn func_from_ast( let f = func { result = lookup(store, ft.result)?, variadism = switch (ft.variadism) { - ast::variadism::NONE => variadism::NONE, - ast::variadism::C => variadism::C, - ast::variadism::HARE => variadism::HARE, + case ast::variadism::NONE => + yield variadism::NONE; + case ast::variadism::C => + yield variadism::C; + case ast::variadism::HARE => + yield variadism::HARE; }, flags = 0, params = alloc([], len(ft.params)), @@ -326,41 +319,40 @@ fn list_from_ast( let sz = SIZE_UNDEFINED, align = SIZE_UNDEFINED; let memb = lookup(store, lt.members)?; let t = match (lt.length) { - ast::len_slice => { - sz = store.arch._pointer; - if (sz % store.arch._size != 0) { - sz += store.arch._size - (sz % store.arch._size); - }; - sz += store.arch._size * 2; - align = if (store.arch._pointer > store.arch._size) - store.arch._pointer - else store.arch._size; - yield memb: slice; - }, + case ast::len_slice => + sz = store.arch._pointer; + if (sz % store.arch._size != 0) { + sz += store.arch._size - (sz % store.arch._size); + }; + sz += store.arch._size * 2; + align = if (store.arch._pointer > store.arch._size) + store.arch._pointer + else store.arch._size; + yield memb: slice; + case (ast::len_unbounded | ast::len_contextual) => // Note: contextual length is handled by hare::unit when // initializing bindings. We treat it like unbounded here and // it's fixed up later on. - (ast::len_unbounded | ast::len_contextual) => { - align = memb.align; - yield array { - length = SIZE_UNDEFINED, - member = memb, - }; - }, - ex: *ast::expr => { - const resolv = match (store.resolve) { - null => return noresolver, - r: *resolver => r, - }; - const length = resolv(store.rstate, store, ex)?; - sz = memb.sz * length; - assert(sz / length == memb.sz, "overflow"); - align = memb.align; - yield array { - length = length, - member = memb, - }; - }, + align = memb.align; + yield array { + length = SIZE_UNDEFINED, + member = memb, + }; + case ex: *ast::expr => + const resolv = match (store.resolve) { + case null => + return noresolver; + case r: *resolver => + yield r; + }; + const length = resolv(store.rstate, store, ex)?; + sz = memb.sz * length; + assert(sz / length == memb.sz, "overflow"); + align = memb.align; + yield array { + length = length, + member = memb, + }; }; return (sz, align, t); }; @@ -375,27 +367,35 @@ fn _struct_from_ast( const nfields = len(fields); for (let i = 0z; i < len(membs); i += 1) { *offs = match (membs[i]._offset) { - ex: *ast::expr => match (store.resolve) { - null => return noresolver, - res: *resolver => res(store.rstate, store, ex)?, - }, - null => *offs, + case ex: *ast::expr => + yield match (store.resolve) { + case null => + return noresolver; + case res: *resolver => + yield res(store.rstate, store, ex)?; + }; + case null => + yield *offs; }; const memb = match (membs[i].member) { - se: ast::struct_embedded => { - let membs: []ast::struct_member = match (se.repr) { - st: ast::struct_type => st, - ut: ast::union_type => ut, - * => abort(), // Invariant - }; - _struct_from_ast(store, membs, - se.repr is ast::union_type, - fields, offs)?; - continue; - }, - se: ast::struct_alias => abort(), // TODO - sf: ast::struct_field => sf, + case se: ast::struct_embedded => + let membs: []ast::struct_member = match (se.repr) { + case st: ast::struct_type => + yield st; + case ut: ast::union_type => + yield ut; + case => + abort(); // Invariant + }; + _struct_from_ast(store, membs, + se.repr is ast::union_type, + fields, offs)?; + continue; + case se: ast::struct_alias => + abort(); // TODO + case sf: ast::struct_field => + yield sf; }; const _type = lookup(store, memb._type)?; @@ -446,8 +446,10 @@ fn tagged_collect( types: *[]const *_type, ) (void | deferred | error) = { for (let i = 0z; i < len(atype); i += 1) match (atype[i].repr) { - ta: ast::tagged_type => tagged_collect(store, ta, types)?, - * => append(types, lookup(store, atype[i])?), + case ta: ast::tagged_type => + tagged_collect(store, ta, types)?; + case => + append(types, lookup(store, atype[i])?); }; }; @@ -506,15 +508,21 @@ fn field_cmp(a: const *void, b: const *void) int = { fn type_finish(t: *_type) void = { match (t.repr) { - a: alias => ast::ident_free(a.id), - array => void, - builtin => void, - e: _enum => free(e.values), - f: func => free(f.params), - pointer => void, - s: slice => void, - st: _struct => free(st.fields), - tu: tuple => free(tu), - ta: tagged => free(ta), + case a: alias => + ast::ident_free(a.id); + case array => void; + case builtin => void; + case e: _enum => + free(e.values); + case f: func => + free(f.params); + case pointer => void; + case s: slice => void; + case st: _struct => + free(st.fields); + case tu: tuple => + free(tu); + case ta: tagged => + free(ta); }; }; diff --git a/hare/unit/process.ha b/hare/unit/process.ha @@ -13,8 +13,10 @@ fn process(ctx: *context, subunits: const []ast::subunit) (unit | error) = { for (let j = 0z; j < len(subunit.decls); j += 1) { let adecl = &subunit.decls[j]; let decl = match (process_decl(ctx, adecl)) { - d: decl => d, - error => abort(), // TODO + case d: decl => + yield d; + case error => + abort(); // TODO }; append(unit.decls, decl); }; @@ -28,11 +30,15 @@ fn process_decl( decl: *ast::decl, ) (decl | error) = { // TODO: match on &decl.decl - return match (decl.decl) { - co: []ast::decl_const => abort(), // TODO - gl: []ast::decl_global => abort(), // TODO - ty: []ast::decl_type => abort(), // TODO - fu: ast::decl_func => process_func(ctx, decl, fu), + match (decl.decl) { + case co: []ast::decl_const => + abort(); // TODO + case gl: []ast::decl_global => + abort(); // TODO + case ty: []ast::decl_type => + abort(); // TODO + case fu: ast::decl_func => + return process_func(ctx, decl, fu); }; }; @@ -50,8 +56,10 @@ fn process_func( ctx.fntype = &fntype; const body: nullable *expr = match (afndecl.body) { - abody: ast::expr => process_expr(ctx, &abody)?, - void => null, + case abody: ast::expr => + yield process_expr(ctx, &abody)?; + case void => + yield null; }; return decl { @@ -72,49 +80,82 @@ fn process_func( fn process_expr( ctx: *context, expr: *ast::expr, -) (*expr | error) = match (expr.expr) { - ast::access_expr => process_access(ctx, expr), - ast::alloc_expr => abort(), // TODO - ast::append_expr => abort(), // TODO - ast::assert_expr => abort(), // TODO - ast::assign_expr => abort(), // TODO - ast::binarithm_expr => abort(), // TODO - ast::binding_expr => process_binding(ctx, expr), - ast::break_expr => abort(), // TODO - ast::call_expr => abort(), // TODO - ast::cast_expr => abort(), // TODO - ast::compound_expr => process_compound(ctx, expr), - ast::constant_expr => process_constant(ctx, expr), - ast::continue_expr => abort(), // TODO - ast::defer_expr => abort(), // TODO - ast::delete_expr => abort(), // TODO - ast::for_expr => abort(), // TODO - ast::free_expr => abort(), // TODO - ast::if_expr => abort(), // TODO - ast::match_expr => abort(), // TODO - ast::len_expr => abort(), // TODO - ast::size_expr => abort(), // TODO - ast::offset_expr => abort(), // TODO - ast::propagate_expr => abort(), // TODO - ast::return_expr => process_return(ctx, expr), - ast::slice_expr => abort(), // TODO - ast::switch_expr => abort(), // TODO - ast::unarithm_expr => abort(), // TODO +) (*expr | error) = { + match (expr.expr) { + case ast::access_expr => + return process_access(ctx, expr); + case ast::alloc_expr => + abort(); // TODO + case ast::append_expr => + abort(); // TODO + case ast::assert_expr => + abort(); // TODO + case ast::assign_expr => + abort(); // TODO + case ast::binarithm_expr => + abort(); // TODO + case ast::binding_expr => + return process_binding(ctx, expr); + case ast::break_expr => + abort(); // TODO + case ast::call_expr => + abort(); // TODO + case ast::cast_expr => + abort(); // TODO + case ast::compound_expr => + return process_compound(ctx, expr); + case ast::constant_expr => + return process_constant(ctx, expr); + case ast::continue_expr => + abort(); // TODO + case ast::defer_expr => + abort(); // TODO + case ast::delete_expr => + abort(); // TODO + case ast::for_expr => + abort(); // TODO + case ast::free_expr => + abort(); // TODO + case ast::if_expr => + abort(); // TODO + case ast::match_expr => + abort(); // TODO + case ast::len_expr => + abort(); // TODO + case ast::size_expr => + abort(); // TODO + case ast::offset_expr => + abort(); // TODO + case ast::propagate_expr => + abort(); // TODO + case ast::return_expr => + return process_return(ctx, expr); + case ast::slice_expr => + abort(); // TODO + case ast::switch_expr => + abort(); // TODO + case ast::unarithm_expr => + abort(); // TODO + }; }; fn process_access(ctx: *context, aexpr: *ast::expr) (*expr | error) = { const access = aexpr.expr as ast::access_expr; const op: (const *types::_type, access) = match (access) { - ai: ast::access_identifier => { - const object = match (ctx_lookup(ctx, ai)) { - null => abort(), // TODO: Error - obj: *object => obj, - }; - yield (object._type, object); - }, - ai: ast::access_index => abort(), // TODO - af: ast::access_field => abort(), // TODO - at: ast::access_tuple => abort(), // TODO + case ai: ast::access_identifier => + const object = match (ctx_lookup(ctx, ai)) { + case null => + abort(); // TODO: Error + case obj: *object => + yield obj; + }; + yield (object._type, object); + case ai: ast::access_index => + abort(); // TODO + case af: ast::access_field => + abort(); // TODO + case at: ast::access_tuple => + abort(); // TODO }; return alloc(expr { start = aexpr.start, @@ -152,8 +193,10 @@ fn process_binding(ctx: *context, aexpr: *ast::expr) (*expr | error) = { const item = bind.bindings[i]; const init = process_expr(ctx, item.init)?; const _type = match (item._type) { - null => abort(), // TODO - ty: *ast::_type => types::lookup(ctx.store, ty)!, + case null => + abort(); // TODO + case ty: *ast::_type => + yield types::lookup(ctx.store, ty)!; }; const object = scope_insert(ctx, object { kind = object_kind::BIND, @@ -219,23 +262,35 @@ fn process_constant(ctx: *context, aexpr: *ast::expr) (*expr | error) = { const constexpr = aexpr.expr as ast::constant_expr; // TODO: Tuple unpacking const er: (const *types::_type, constant) = match (constexpr) { - v: ast::value => ( + case v: ast::value => + yield ( // TODO: iconst/fconst lowering types::lookup_builtin(ctx.store, match (v) { - ast::_null => ast::builtin_type::NULL, - b: bool => ast::builtin_type::BOOL, - s: str => ast::builtin_type::STR, - r: rune => ast::builtin_type::RUNE, - i: i64 => ast::builtin_type::INT, - u: u64 => ast::builtin_type::UINT, - f: f64 => ast::builtin_type::F64, - void => ast::builtin_type::VOID, + case ast::_null => + yield ast::builtin_type::NULL; + case b: bool => + yield ast::builtin_type::BOOL; + case s: str => + yield ast::builtin_type::STR; + case r: rune => + yield ast::builtin_type::RUNE; + case i: i64 => + yield ast::builtin_type::INT; + case u: u64 => + yield ast::builtin_type::UINT; + case f: f64 => + yield ast::builtin_type::F64; + case void => + yield ast::builtin_type::VOID; }), v, - ), - ast::array_constant => abort(), // TODO - ast::struct_constant => abort(), // TODO - ast::tuple_constant => abort(), // TODO + ); + case ast::array_constant => + abort(); // TODO + case ast::struct_constant => + abort(); // TODO + case ast::tuple_constant => + abort(); // TODO }; return alloc(expr { start = aexpr.start, @@ -284,19 +339,25 @@ fn process_constant(ctx: *context, aexpr: *ast::expr) (*expr | error) = { ("13.37", types::builtin::F64, 13.37f64), ]; for (let i = 0z; i < len(cases); i += 1) { - const case = cases[i]; - const aexpr = parse_expr(case.0); + const _case = cases[i]; + const aexpr = parse_expr(_case.0); defer ast::expr_free(aexpr); const expr = process_constant(&ctx, aexpr)!; - assert(expr.result.repr as types::builtin == case.1); + assert(expr.result.repr as types::builtin == _case.1); const constexpr = expr.expr as constant; - match (case.2) { - s: str => assert(constexpr as str == s), - r: rune => assert(constexpr as rune == r), - i: i64 => assert(constexpr as i64 == i), - u: u64 => assert(constexpr as u64 == u), - f: f64 => assert(constexpr as f64 == f), - void => abort(), + match (_case.2) { + case s: str => + assert(constexpr as str == s); + case r: rune => + assert(constexpr as rune == r); + case i: i64 => + assert(constexpr as i64 == i); + case u: u64 => + assert(constexpr as u64 == u); + case f: f64 => + assert(constexpr as f64 == f); + case void => + abort(); }; }; }; @@ -304,8 +365,10 @@ fn process_constant(ctx: *context, aexpr: *ast::expr) (*expr | error) = { fn process_return(ctx: *context, aexpr: *ast::expr) (*expr | error) = { const ret = aexpr.expr as ast::return_expr; const rval = match (ret) { - null => null, - aexpr: *ast::expr => process_expr(ctx, aexpr)?, + case null => + yield null; + case aexpr: *ast::expr => + yield process_expr(ctx, aexpr)?; }; // TODO: assert(types::assignable(ctx.fntype.result, rval.type)); return alloc(expr { diff --git a/hare/unit/scan.ha b/hare/unit/scan.ha @@ -12,9 +12,11 @@ fn scan(ctx: *context, subunits: const []ast::subunit) (void | error) = { for (let j = 0z; j < len(subunit.decls); j += 1) { let decl = &subunit.decls[j]; match (scan_decl(ctx, decl)) { - void => void, - types::deferred => abort(), // TODO - error => abort(), // TODO + case void => void; + case types::deferred => + abort(); // TODO + case error => + abort(); // TODO }; }; scope_pop(ctx); @@ -26,11 +28,15 @@ fn scan_decl( decl: *ast::decl, ) (void | types::deferred | error) = { // TODO: match on &decl.decl - return match (decl.decl) { - co: []ast::decl_const => abort(), // TODO - gl: []ast::decl_global => abort(), // TODO - ty: []ast::decl_type => abort(), // TODO - fu: ast::decl_func => scan_func(ctx, decl, fu), + match (decl.decl) { + case co: []ast::decl_const => + abort(); // TODO + case gl: []ast::decl_global => + abort(); // TODO + case ty: []ast::decl_type => + abort(); // TODO + case fu: ast::decl_func => + return scan_func(ctx, decl, fu); }; }; @@ -41,9 +47,12 @@ fn scan_func( ) (void | types::deferred | error) = { assert(func.attrs & ast::fndecl_attrs::TEST == 0); // TODO const fntype = match (types::lookup(ctx.store, &func.prototype)) { - err: types::error => return err, - types::deferred => return types::deferred, - fntype: const *types::_type => fntype, + case err: types::error => + return err; + case types::deferred => + return types::deferred; + case fntype: const *types::_type => + yield fntype; }; scope_insert(ctx, object { kind = object_kind::DECL, diff --git a/hare/unit/scope.ha b/hare/unit/scope.ha @@ -97,8 +97,10 @@ fn scope_lookup(scope: *scope, ident: ast::ident) nullable *object = { return bucket[i]; }; }; - return match (scope.parent) { - null => null, - scope: *scope => scope_lookup(scope, ident), + match (scope.parent) { + case null => + return null; + case scope: *scope => + return scope_lookup(scope, ident); }; }; diff --git a/hare/unparse/decl.ha b/hare/unparse/decl.ha @@ -10,80 +10,78 @@ export fn decl(out: *io::stream, d: ast::decl) (size | io::error) = { n += fmt::fprint(out, "export ")?; }; match (d.decl) { - c: []ast::decl_const => { - n += fmt::fprint(out, "def ")?; - for (let i = 0z; i < len(c); i += 1) { - n += ident(out, c[i].ident)?; - n += fmt::fprint(out, ": ")?; - n += _type(out, 0, c[i]._type)?; - n += fmt::fprint(out, " = ")?; - n += expr(out, 0, *c[i].init)?; - if (i + 1 < len(c)) { - n += fmt::fprint(out, ", ")?; - }; + case c: []ast::decl_const => + n += fmt::fprint(out, "def ")?; + for (let i = 0z; i < len(c); i += 1) { + n += ident(out, c[i].ident)?; + n += fmt::fprint(out, ": ")?; + n += _type(out, 0, c[i]._type)?; + n += fmt::fprint(out, " = ")?; + n += expr(out, 0, *c[i].init)?; + if (i + 1 < len(c)) { + n += fmt::fprint(out, ", ")?; }; - }, - g: []ast::decl_global => { - n += fmt::fprint(out, - if (g[0].is_const) "const " else "let ")?; - for (let i = 0z; i < len(g); i += 1) { - if (len(g[i].symbol) != 0) { - n += fmt::fprintf(out, - "@symbol(\"{}\") ", g[i].symbol)?; - }; - n += ident(out, g[i].ident)?; - n += fmt::fprint(out, ": ")?; - n += _type(out, 0, g[i]._type)?; - match (g[i].init) { - null => void, - ex: *ast::expr => { - n += fmt::fprint(out, " = ")?; - n += expr(out, 0, *ex)?; - }, - }; - if (i + 1 < len(g)) { - n += fmt::fprint(out, ", ")?; - }; + }; + case g: []ast::decl_global => + n += fmt::fprint(out, + if (g[0].is_const) "const " else "let ")?; + for (let i = 0z; i < len(g); i += 1) { + if (len(g[i].symbol) != 0) { + n += fmt::fprintf(out, + "@symbol(\"{}\") ", g[i].symbol)?; }; - }, - t: []ast::decl_type => { - n += fmt::fprint(out, "type ")?; - for (let i = 0z; i < len(t); i += 1) { - n += ident(out, t[i].ident)?; + n += ident(out, g[i].ident)?; + n += fmt::fprint(out, ": ")?; + n += _type(out, 0, g[i]._type)?; + match (g[i].init) { + case null => void; + case ex: *ast::expr => n += fmt::fprint(out, " = ")?; - n += _type(out, 0, t[i]._type)?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, ", ")?; - }; + n += expr(out, 0, *ex)?; }; - }, - f: ast::decl_func => { - n += fmt::fprint(out, switch (f.attrs) { - ast::fndecl_attrs::NONE => "", - ast::fndecl_attrs::FINI => "@fini ", - ast::fndecl_attrs::INIT => "@init ", - ast::fndecl_attrs::TEST => "@test ", - })?; - let p = f.prototype.repr as ast::func_type; - if (p.attrs & ast::func_attrs::NORETURN != 0) { - n += fmt::fprint(out, "@noreturn ")?; + if (i + 1 < len(g)) { + n += fmt::fprint(out, ", ")?; }; - if (len(f.symbol) != 0) { - n += fmt::fprintf(out, "@symbol(\"{}\") ", - f.symbol)?; + }; + case t: []ast::decl_type => + n += fmt::fprint(out, "type ")?; + for (let i = 0z; i < len(t); i += 1) { + n += ident(out, t[i].ident)?; + n += fmt::fprint(out, " = ")?; + n += _type(out, 0, t[i]._type)?; + if (i + 1 < len(t)) { + n += fmt::fprint(out, ", ")?; }; - n += fmt::fprint(out, "fn ")?; - n += ident(out, f.ident)?; - const fntype = f.prototype.repr as ast::func_type; - n += prototype(out, 0, fntype)?; - match (f.body) { - void => void, - e: ast::expr => { - n += fmt::fprint(out, " = ")?; - n += expr(out, 0, e)?; - }, - }; - }, + }; + case f: ast::decl_func => + n += fmt::fprint(out, switch (f.attrs) { + case ast::fndecl_attrs::NONE => + yield ""; + case ast::fndecl_attrs::FINI => + yield "@fini "; + case ast::fndecl_attrs::INIT => + yield "@init "; + case ast::fndecl_attrs::TEST => + yield "@test "; + })?; + let p = f.prototype.repr as ast::func_type; + if (p.attrs & ast::func_attrs::NORETURN != 0) { + n += fmt::fprint(out, "@noreturn ")?; + }; + if (len(f.symbol) != 0) { + n += fmt::fprintf(out, "@symbol(\"{}\") ", + f.symbol)?; + }; + n += fmt::fprint(out, "fn ")?; + n += ident(out, f.ident)?; + const fntype = f.prototype.repr as ast::func_type; + n += prototype(out, 0, fntype)?; + match (f.body) { + case void => void; + case e: ast::expr => + n += fmt::fprint(out, " = ")?; + n += expr(out, 0, e)?; + }; }; n += fmt::fprint(out, ";")?; return n; diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha @@ -13,358 +13,374 @@ export fn expr( indent: size, e: ast::expr ) (size | io::error) = { - return match (e.expr) { - e: ast::access_expr => match (e) { - id: ast::access_identifier => ident(out, id), - ix: ast::access_index => { - let z = expr(out, indent, *ix.object)?; - z += fmt::fprintf(out, "[")?; - z += expr(out, indent, *ix.index)?; - z += fmt::fprintf(out, "]")?; - yield z; - }, - fi: ast::access_field => { - let z = expr(out, indent, *fi.object)?; - z += fmt::fprintf(out, ".{}", fi.field)?; - yield z; - }, - tp: ast::access_tuple => { - let z = expr(out, indent, *tp.object)?; - z += fmt::fprintf(out, ".")?; - z += expr(out, indent, *tp.value)?; - yield z; - }, - }, - e: ast::alloc_expr => { - let z = fmt::fprint(out, "alloc(")?; - z += expr(out, indent, *e.init)?; - match (e.capacity) { - null => void, - e: *ast::expr => { - z += fmt::fprint(out, ", ")?; - z += expr(out, indent, *e)?; - }, - }; - z += fmt::fprint(out, ")")?; - yield z; - }, - e: ast::append_expr => { - let z = if (e.is_static) fmt::fprint(out, "static ")? - else 0z; - z += fmt::fprint(out, "append(")?; - z += expr(out, indent, *e.object)?; + match (e.expr) { + case e: ast::access_expr => + match (e) { + case id: ast::access_identifier => + return ident(out, id); + case ix: ast::access_index => + let z = expr(out, indent, *ix.object)?; + z += fmt::fprintf(out, "[")?; + z += expr(out, indent, *ix.index)?; + z += fmt::fprintf(out, "]")?; + return z; + case fi: ast::access_field => + let z = expr(out, indent, *fi.object)?; + z += fmt::fprintf(out, ".{}", fi.field)?; + return z; + case tp: ast::access_tuple => + let z = expr(out, indent, *tp.object)?; + z += fmt::fprintf(out, ".")?; + z += expr(out, indent, *tp.value)?; + return z; + }; + case e: ast::alloc_expr => + let z = fmt::fprint(out, "alloc(")?; + z += expr(out, indent, *e.init)?; + match (e.capacity) { + case null => void; + case e: *ast::expr => z += fmt::fprint(out, ", ")?; - for (let i = 0z; i < len(e.values); i += 1) { - let val = e.values[i]; - z += expr(out, indent, *val)?; - if (i + 1 < len(e.values)) { - z += fmt::fprint(out, ", ")?; - }; - }; - match (e.variadic) { - null => void, - v: *ast::expr => { - if (len(e.values) != 0) { - z += fmt::fprint(out, ", ")?; - }; - z += expr(out, indent, *v)?; - z += fmt::fprint(out, "...")?; - }, - }; - z += fmt::fprint(out, ")")?; - yield z; - }, - e: ast::assert_expr => { - let z = fmt::fprint( - out, if (e.is_static) "static " else "")?; - // assert without a condition = abort - z += match (e.cond) { - e: *ast::expr => { - yield fmt::fprint(out, "assert(")? + - expr(out, indent, *e)?; - }, - null => fmt::fprint(out, "abort(")?, + z += expr(out, indent, *e)?; + }; + z += fmt::fprint(out, ")")?; + return z; + case e: ast::append_expr => + let z = if (e.is_static) fmt::fprint(out, "static ")? else 0z; + z += fmt::fprint(out, "append(")?; + z += expr(out, indent, *e.object)?; + z += fmt::fprint(out, ", ")?; + for (let i = 0z; i < len(e.values); i += 1) { + let val = e.values[i]; + z += expr(out, indent, *val)?; + if (i + 1 < len(e.values)) { + z += fmt::fprint(out, ", ")?; }; - z += match (e.message) { - m: *ast::expr => { - let z = 0z; - match (e.cond) { - *ast::expr => { - z += fmt::fprint(out, ", ")?; - }, - null => void, - }; - z += expr(out, indent, *m)?; - yield z; - }, - null => 0, + }; + match (e.variadic) { + case null => void; + case v: *ast::expr => + if (len(e.values) != 0) { + z += fmt::fprint(out, ", ")?; }; - z += fmt::fprint(out, ")")?; - yield z; - }, - e: ast::assign_expr => { + z += expr(out, indent, *v)?; + z += fmt::fprint(out, "...")?; + }; + z += fmt::fprint(out, ")")?; + return z; + case e: ast::assert_expr => + let z = fmt::fprint( + out, if (e.is_static) "static " else "")?; + // assert without a condition = abort + z += match (e.cond) { + case e: *ast::expr => + yield fmt::fprint(out, "assert(")? + + expr(out, indent, *e)?; + case null => + yield fmt::fprint(out, "abort(")?; + }; + z += match (e.message) { + case m: *ast::expr => let z = 0z; - if (e.indirect) { - z += fmt::fprint(out, "*")?; - }; - z += expr(out, indent, *e.object)?; - const op = match (e.op) { - void => "=", - op: ast::binarithm_op => switch (op) { - ast::binarithm_op::BAND => "&=", - ast::binarithm_op::LAND => "&&=", - ast::binarithm_op::BOR => "|=", - ast::binarithm_op::LOR => "||=", - ast::binarithm_op::DIV => "/=", - ast::binarithm_op::LSHIFT => "<<=", - ast::binarithm_op::MINUS => "-=", - ast::binarithm_op::MODULO => "%=", - ast::binarithm_op::PLUS => "+=", - ast::binarithm_op::RSHIFT => ">>=", - ast::binarithm_op::TIMES => "*=", - ast::binarithm_op::BXOR => "^=", - ast::binarithm_op::LXOR => "^^=", - * => abort(), - }, - }; - z += fmt::fprintf(out, " {} ", op)?; - z += expr(out, indent, *e.value)?; - yield z; - }, - e: ast::binarithm_expr => { - let z = expr(out, indent, *e.lvalue)?; - z += fmt::fprintf(out, " {} ", switch (e.op) { - ast::binarithm_op::BAND => "&", - ast::binarithm_op::BOR => "|", - ast::binarithm_op::DIV => "/", - ast::binarithm_op::GT => ">", - ast::binarithm_op::GTEQ => ">=", - ast::binarithm_op::LAND => "&&", - ast::binarithm_op::LEQUAL => "==", - ast::binarithm_op::LESS => "<", - ast::binarithm_op::LESSEQ => "<=", - ast::binarithm_op::LOR => "||", - ast::binarithm_op::LSHIFT => "<<", - ast::binarithm_op::LXOR => "^^", - ast::binarithm_op::MINUS => "-", - ast::binarithm_op::MODULO => "%", - ast::binarithm_op::NEQUAL => "!=", - ast::binarithm_op::PLUS => "+", - ast::binarithm_op::RSHIFT => ">>", - ast::binarithm_op::TIMES => "*", - ast::binarithm_op::BXOR => "^", - })?; - z += expr(out, indent, *e.rvalue)?; - yield z; - }, - e: ast::binding_expr => { - let z = fmt::fprintf(out, "{}{}", - if (e.is_static) "static " else "", - if (e.is_const) "const " else "let ")?; - for (let i = 0z; i < len(e.bindings); i += 1) { - let binding = e.bindings[i]; - z += match (binding._type) { - null => fmt::fprint(out, binding.name)?, - t: *ast::_type => { - let z = 0z; - z += fmt::fprintf(out, "{}: ", - binding.name)?; - z += _type(out, indent, *t)?; - yield z; - }, - }; - z += fmt::fprint(out, " = ")?; - z += expr(out, indent, *binding.init)?; - if (i + 1 < len(e.bindings)) { - z += fmt::fprint(out, ", ")?; - }; + match (e.cond) { + case null => void; + case *ast::expr => + z += fmt::fprint(out, ", ")?; }; + z += expr(out, indent, *m)?; yield z; - }, - e: ast::break_expr => { - let z = fmt::fprint(out, "break")?; - if (e != "") { - z += fmt::fprintf(out, " :{}", e)?; + case null => + yield 0; + }; + z += fmt::fprint(out, ")")?; + return z; + case e: ast::assign_expr => + let z = 0z; + if (e.indirect) { + z += fmt::fprint(out, "*")?; + }; + z += expr(out, indent, *e.object)?; + const op = match (e.op) { + case void => + yield "="; + case op: ast::binarithm_op => + yield switch (op) { + case ast::binarithm_op::BAND => + yield "&="; + case ast::binarithm_op::LAND => + yield "&&="; + case ast::binarithm_op::BOR => + yield "|="; + case ast::binarithm_op::LOR => + yield "||="; + case ast::binarithm_op::DIV => + yield "/="; + case ast::binarithm_op::LSHIFT => + yield "<<="; + case ast::binarithm_op::MINUS => + yield "-="; + case ast::binarithm_op::MODULO => + yield "%="; + case ast::binarithm_op::PLUS => + yield "+="; + case ast::binarithm_op::RSHIFT => + yield ">>="; + case ast::binarithm_op::TIMES => + yield "*="; + case ast::binarithm_op::BXOR => + yield "^="; + case ast::binarithm_op::LXOR => + yield "^^="; }; - yield z; - }, - e: ast::call_expr => { - let z = expr(out, indent, *e.lvalue)?; - z += fmt::fprintf(out, "(")?; - for (let i = 0z; i < len(e.args); i += 1) { - z += expr(out, indent, *e.args[i])?; - if (i + 1 < len(e.args)) { - z += fmt::fprintf(out, ", ")?; - }; + }; + z += fmt::fprintf(out, " {} ", op)?; + z += expr(out, indent, *e.value)?; + return z; + case e: ast::binarithm_expr => + let z = expr(out, indent, *e.lvalue)?; + z += fmt::fprintf(out, " {} ", switch (e.op) { + case ast::binarithm_op::BAND => + yield "&"; + case ast::binarithm_op::BOR => + yield "|"; + case ast::binarithm_op::DIV => + yield "/"; + case ast::binarithm_op::GT => + yield ">"; + case ast::binarithm_op::GTEQ => + yield ">="; + case ast::binarithm_op::LAND => + yield "&&"; + case ast::binarithm_op::LEQUAL => + yield "=="; + case ast::binarithm_op::LESS => + yield "<"; + case ast::binarithm_op::LESSEQ => + yield "<="; + case ast::binarithm_op::LOR => + yield "||"; + case ast::binarithm_op::LSHIFT => + yield "<<"; + case ast::binarithm_op::LXOR => + yield "^^"; + case ast::binarithm_op::MINUS => + yield "-"; + case ast::binarithm_op::MODULO => + yield "%"; + case ast::binarithm_op::NEQUAL => + yield "!="; + case ast::binarithm_op::PLUS => + yield "+"; + case ast::binarithm_op::RSHIFT => + yield ">>"; + case ast::binarithm_op::TIMES => + yield "*"; + case ast::binarithm_op::BXOR => + yield "^"; + })?; + z += expr(out, indent, *e.rvalue)?; + return z; + case e: ast::binding_expr => + let z = fmt::fprintf(out, "{}{}", + if (e.is_static) "static " else "", + if (e.is_const) "const " else "let ")?; + for (let i = 0z; i < len(e.bindings); i += 1) { + let binding = e.bindings[i]; + z += match (binding._type) { + case null => + yield fmt::fprint(out, binding.name)?; + case t: *ast::_type => + let z = 0z; + z += fmt::fprintf(out, "{}: ", + binding.name)?; + z += _type(out, indent, *t)?; + yield z; }; - if (e.variadic) { - z += fmt::fprintf(out, "...")?; + z += fmt::fprint(out, " = ")?; + z += expr(out, indent, *binding.init)?; + if (i + 1 < len(e.bindings)) { + z += fmt::fprint(out, ", ")?; }; - z += fmt::fprintf(out, ")")?; - yield z; - }, - e: ast::cast_expr => { - let z = expr(out, indent, *e.value)?; - const op = switch (e.kind) { - ast::cast_kind::CAST => ": ", - ast::cast_kind::ASSERTION => " as ", - ast::cast_kind::TEST => " is ", - }; - z += fmt::fprintf(out, "{}", op)?; - z += _type(out, indent, *e._type)?; - yield z; - }, - e: ast::constant_expr => constant(out, indent, e)?, - e: ast::continue_expr => { - let z = fmt::fprint(out, "continue")?; - if (e != "") { - z += fmt::fprintf(out, " :{}", e)?; + }; + return z; + case e: ast::break_expr => + let z = fmt::fprint(out, "break")?; + if (e != "") { + z += fmt::fprintf(out, " :{}", e)?; + }; + return z; + case e: ast::call_expr => + let z = expr(out, indent, *e.lvalue)?; + z += fmt::fprintf(out, "(")?; + for (let i = 0z; i < len(e.args); i += 1) { + z += expr(out, indent, *e.args[i])?; + if (i + 1 < len(e.args)) { + z += fmt::fprintf(out, ", ")?; }; - yield z; - }, - e: ast::defer_expr => - fmt::fprint(out, "defer ")? + expr(out, indent, *e)?, - e: ast::delete_expr => { - let z = if (e.is_static) fmt::fprint(out, "static ")? - else 0z; - z += fmt::fprint(out, "delete(")?; - z += expr(out, indent, *e.object)?; - z += fmt::fprint(out, ")")?; - yield z; - }, - e: ast::for_expr => for_expr(out, indent, e)?, - e: ast::free_expr => - fmt::fprint(out, "free(")? + }; + if (e.variadic) { + z += fmt::fprintf(out, "...")?; + }; + z += fmt::fprintf(out, ")")?; + return z; + case e: ast::cast_expr => + let z = expr(out, indent, *e.value)?; + const op = switch (e.kind) { + case ast::cast_kind::CAST => + yield ": "; + case ast::cast_kind::ASSERTION => + yield " as "; + case ast::cast_kind::TEST => + yield " is "; + }; + z += fmt::fprintf(out, "{}", op)?; + z += _type(out, indent, *e._type)?; + return z; + case e: ast::constant_expr => + return constant(out, indent, e)?; + case e: ast::continue_expr => + let z = fmt::fprint(out, "continue")?; + if (e != "") { + z += fmt::fprintf(out, " :{}", e)?; + }; + return z; + case e: ast::defer_expr => + return fmt::fprint(out, "defer ")? + expr(out, indent, *e)?; + case e: ast::delete_expr => + let z = if (e.is_static) fmt::fprint(out, "static ")? else 0z; + z += fmt::fprint(out, "delete(")?; + z += expr(out, indent, *e.object)?; + z += fmt::fprint(out, ")")?; + return z; + case e: ast::for_expr => + return for_expr(out, indent, e)?; + case e: ast::free_expr => + return fmt::fprint(out, "free(")? + expr(out, indent, *e)? - + fmt::fprint(out, ")")?, - e: ast::if_expr => { - let z = fmt::fprint(out, "if (")?; - z += expr(out, indent, *e.cond)?; - z += fmt::fprint(out, ") ")?; - z += expr(out, indent, *e.tbranch)?; - match (e.fbranch) { - null => void, - e: *ast::expr => { - z += fmt::fprint(out, " else ")?; - z += expr(out, indent, *e)?; - }, - }; - yield z; - }, - e: ast::insert_expr => { - let z = if (e.is_static) fmt::fprint(out, "static ")? - else 0z; - z += fmt::fprint(out, "insert(")?; - z += expr(out, indent, *e.object)?; - z += fmt::fprint(out, ", ")?; - for (let i = 0z; i < len(e.values); i += 1) { - let val = e.values[i]; - z += expr(out, indent, *val)?; - if (i + 1 < len(e.values)) { - z += fmt::fprint(out, ", ")?; - }; - }; - match (e.variadic) { - null => void, - v: *ast::expr => { - if (len(e.values) != 0) { - z += fmt::fprint(out, ", ")?; - }; - z += expr(out, indent, *v)?; - z += fmt::fprint(out, "...")?; - }, - }; - z += fmt::fprint(out, ")")?; - yield z; - }, - e: ast::compound_expr => { - let z = 0z; - if (e.label != "") { - z += fmt::fprintf(out, ":{} ", e.label)?; - }; - z += fmt::fprintf(out, "{{")?; - for (let i = 0z; i < len(e.exprs); i += 1) { - z += newline(out, indent + 1)?; - z += expr(out, indent + 1, *e.exprs[i])?; - z += fmt::fprintf(out, ";")?; - }; - z += newline(out, indent)?; - z += fmt::fprintf(out, "}}")?; - yield z; - }, - e: ast::match_expr => match_expr(out, indent, e)?, - e: ast::len_expr => { - let z = fmt::fprint(out, "len(")?; + + fmt::fprint(out, ")")?; + case e: ast::if_expr => + let z = fmt::fprint(out, "if (")?; + z += expr(out, indent, *e.cond)?; + z += fmt::fprint(out, ") ")?; + z += expr(out, indent, *e.tbranch)?; + match (e.fbranch) { + case null => void; + case e: *ast::expr => + z += fmt::fprint(out, " else ")?; z += expr(out, indent, *e)?; - z += fmt::fprint(out, ")")?; - yield z; - }, - e: ast::size_expr => { - let z = fmt::fprint(out, "size(")?; - z += _type(out, indent, *e)?; - z += fmt::fprint(out, ")")?; - yield z; - }, - ast::offset_expr => abort(), - e: ast::propagate_expr => { - let z = expr(out, indent, *e.expr)?; - z += fmt::fprintf(out, if (e.is_abort) "!" else "?")?; - yield z; - }, - e: ast::return_expr => { - let z = fmt::fprint(out, "return")?; - match (e) { - null => void, - e: *ast::expr => { - z += fmt::fprint(out, " ")?; - z += expr(out, indent, *e)?; - }, - }; - yield z; - }, - e: ast::slice_expr => { - let z = expr(out, indent, *e.object)?; - z += fmt::fprint(out, "[")?; - z += match (e.start) { - null => 0z, - e: *ast::expr => expr(out, indent, *e)?, - }; - z += fmt::fprint(out, "..")?; - z += match (e.end) { - null => 0z, - e: *ast::expr => expr(out, indent, *e)?, - }; - z += fmt::fprint(out, "]")?; - yield z; - }, - e: ast::switch_expr => switch_expr(out, indent, e)?, - e: ast::unarithm_expr => { - let z = fmt::fprintf(out, "{}", switch (e.op) { - ast::unarithm_op::ADDR => "&", - ast::unarithm_op::BNOT => "~", - ast::unarithm_op::DEREF => "*", - ast::unarithm_op::LNOT => "!", - ast::unarithm_op::MINUS => "-", - ast::unarithm_op::PLUS => "+", - })?; - z += expr(out, indent, *e.operand)?; - yield z; - }, - e: ast::yield_expr => { - let z = fmt::fprint(out, "yield")?; - if (e.label != "") { - z += fmt::fprintf(out, " :{}", e.label)?; + }; + return z; + case e: ast::insert_expr => + let z = if (e.is_static) fmt::fprint(out, "static ")? else 0z; + z += fmt::fprint(out, "insert(")?; + z += expr(out, indent, *e.object)?; + z += fmt::fprint(out, ", ")?; + for (let i = 0z; i < len(e.values); i += 1) { + let val = e.values[i]; + z += expr(out, indent, *val)?; + if (i + 1 < len(e.values)) { + z += fmt::fprint(out, ", ")?; }; - match (e.value) { - null => void, - v: *ast::expr => { - z += fmt::fprint(out, if (e.label == "") - " " else ", ")?; - z += expr(out, indent, *v)?; - }, + }; + match (e.variadic) { + case null => void; + case v: *ast::expr => + if (len(e.values) != 0) { + z += fmt::fprint(out, ", ")?; }; - yield z; - }, + z += expr(out, indent, *v)?; + z += fmt::fprint(out, "...")?; + }; + z += fmt::fprint(out, ")")?; + return z; + case e: ast::compound_expr => + let z = 0z; + if (e.label != "") { + z += fmt::fprintf(out, ":{} ", e.label)?; + }; + z += fmt::fprintf(out, "{{")?; + for (let i = 0z; i < len(e.exprs); i += 1) { + z += newline(out, indent + 1)?; + z += expr(out, indent + 1, *e.exprs[i])?; + z += fmt::fprintf(out, ";")?; + }; + z += newline(out, indent)?; + z += fmt::fprintf(out, "}}")?; + return z; + case e: ast::match_expr => + return match_expr(out, indent, e)?; + case e: ast::len_expr => + let z = fmt::fprint(out, "len(")?; + z += expr(out, indent, *e)?; + z += fmt::fprint(out, ")")?; + return z; + case e: ast::size_expr => + let z = fmt::fprint(out, "size(")?; + z += _type(out, indent, *e)?; + z += fmt::fprint(out, ")")?; + return z; + case ast::offset_expr => abort(); // TODO + case e: ast::propagate_expr => + let z = expr(out, indent, *e.expr)?; + z += fmt::fprintf(out, if (e.is_abort) "!" else "?")?; + return z; + case e: ast::return_expr => + let z = fmt::fprint(out, "return")?; + match (e) { + case null => void; + case e: *ast::expr => + z += fmt::fprint(out, " ")?; + z += expr(out, indent, *e)?; + }; + return z; + case e: ast::slice_expr => + let z = expr(out, indent, *e.object)?; + z += fmt::fprint(out, "[")?; + match (e.start) { + case null => void; + case e: *ast::expr => + z += expr(out, indent, *e)?; + }; + z += fmt::fprint(out, "..")?; + match (e.end) { + case null => void; + case e: *ast::expr => + z += expr(out, indent, *e)?; + }; + z += fmt::fprint(out, "]")?; + return z; + case e: ast::switch_expr => + return switch_expr(out, indent, e)?; + case e: ast::unarithm_expr => + let z = fmt::fprintf(out, "{}", switch (e.op) { + case ast::unarithm_op::ADDR => + yield "&"; + case ast::unarithm_op::BNOT => + yield "~"; + case ast::unarithm_op::DEREF => + yield "*"; + case ast::unarithm_op::LNOT => + yield "!"; + case ast::unarithm_op::MINUS => + yield "-"; + case ast::unarithm_op::PLUS => + yield "+"; + })?; + z += expr(out, indent, *e.operand)?; + return z; + case e: ast::yield_expr => + let z = fmt::fprint(out, "yield")?; + if (e.label != "") { + z += fmt::fprintf(out, " :{}", e.label)?; + }; + match (e.value) { + case null => void; + case v: *ast::expr => + z += fmt::fprint(out, if (e.label == "") + " " else ", ")?; + z += expr(out, indent, *v)?; + }; + return z; }; }; @@ -373,41 +389,47 @@ fn constant( indent: size, e: ast::constant_expr, ) (size | io::error) = { - return match (e) { - void => fmt::fprint(out, "void"), - v: ast::value => fmt::fprint(out, match (v) { - void => abort(), - ast::_null => "null", - v: (i64 | u64 | f64) => v, - b: bool => return fmt::fprint(out, b), - // TODO: Escape these: - s: str => return fmt::fprintf(out, "\"{}\"", s), - r: rune => return fmt::fprintf(out, "'{}'", r), - }), - ac: ast::array_constant => { - let z = fmt::fprint(out, "[")?; - for (let i = 0z; i < len(ac.values); i += 1) { - z += expr(out, indent, *ac.values[i])?; - if (i + 1 < len(ac.values)) { - z += fmt::fprint(out, ", ")?; - }; + match (e) { + case void => + return fmt::fprint(out, "void"); + case v: ast::value => + return fmt::fprint(out, match (v) { + case void => abort(); + case ast::_null => + yield "null"; + case v: (i64 | u64 | f64) => + yield v; + case b: bool => + return fmt::fprint(out, b); + // TODO: Escape these: + case s: str => + return fmt::fprintf(out, "\"{}\"", s); + case r: rune => + return fmt::fprintf(out, "'{}'", r); + }); + case ac: ast::array_constant => + let z = fmt::fprint(out, "[")?; + for (let i = 0z; i < len(ac.values); i += 1) { + z += expr(out, indent, *ac.values[i])?; + if (i + 1 < len(ac.values)) { + z += fmt::fprint(out, ", ")?; }; - z += fmt::fprintf(out, "{}]", - if (ac.expand) "..." else "")?; - yield z; - }, - sc: ast::struct_constant => struct_constant(out, indent, sc)?, - tu: ast::tuple_constant => { - let z = fmt::fprint(out, "(")?; - for (let i = 0z; i < len(tu); i += 1) { - z += expr(out, indent, *tu[i])?; - if (i + 1 < len(tu)) { - z += fmt::fprint(out, ", ")?; - }; + }; + z += fmt::fprintf(out, "{}]", + if (ac.expand) "..." else "")?; + return z; + case sc: ast::struct_constant => + return struct_constant(out, indent, sc)?; + case tu: ast::tuple_constant => + let z = fmt::fprint(out, "(")?; + for (let i = 0z; i < len(tu); i += 1) { + z += expr(out, indent, *tu[i])?; + if (i + 1 < len(tu)) { + z += fmt::fprint(out, ", ")?; }; - z += fmt::fprint(out, ")")?; - yield z; - }, + }; + z += fmt::fprint(out, ")")?; + return z; }; }; @@ -427,19 +449,18 @@ fn struct_constant( for (let i = 0z; i < len(sc.fields); i += 1) { newline(out, indent)?; match (sc.fields[i]) { - sv: ast::struct_value => { - z += match (sv._type) { - null => fmt::fprintf(out, "{}", sv.name)?, - t: *ast::_type => - fmt::fprintf(out, "{}: ", sv.name)? - + _type(out, indent, *t)?, - }; - z += fmt::fprint(out, " = ")?; - z += expr(out, indent, *sv.init)?; - }, - sc: *ast::struct_constant => { - z += constant(out, indent, *sc)?; - }, + case sv: ast::struct_value => + match (sv._type) { + case null => + z += fmt::fprintf(out, "{}", sv.name)?; + case t: *ast::_type => + z += fmt::fprintf(out, "{}: ", sv.name)?; + z += _type(out, indent, *t)?; + }; + z += fmt::fprint(out, " = ")?; + z += expr(out, indent, *sv.init)?; + case sc: *ast::struct_constant => + z += constant(out, indent, *sc)?; }; z += fmt::fprint(out, ",")?; }; @@ -459,17 +480,22 @@ fn for_expr( e: ast::for_expr, ) (size | io::error) = { let z = fmt::fprintf(out, "for (")?; - z += match (e.bindings) { - null => 0z, - e: *ast::expr => expr(out, indent, *e)? - + fmt::fprint(out, "; ")?, + match (e.bindings) { + case null => void; + case e: *ast::expr => + z += expr(out, indent, *e)?; + z += fmt::fprint(out, "; ")?; }; + z += expr(out, indent, *e.cond)?; - z += match (e.afterthought) { - null => 0z, - e: *ast::expr => fmt::fprint(out, "; ")? - + expr(out, indent, *e)?, + + match (e.afterthought) { + case null => void; + case e: *ast::expr => + z += fmt::fprint(out, "; ")?; + z += expr(out, indent, *e)?; }; + z += fmt::fprintf(out, ") ")?; return z + expr(out, indent, *e.body)?; }; @@ -486,21 +512,25 @@ fn switch_expr( for (let i = 0z; i < len(e.cases); i += 1) { z += newline(out, indent)?; - const case = e.cases[i]; - if (len(case.options) == 0) { - z += fmt::fprint(out, "* => ")?; + const item = e.cases[i]; + z += fmt::fprint(out, "case ")?; + if (len(item.options) == 0) { + z += fmt::fprint(out, "=>")?; } else { - for (let j = 0z; j < len(case.options); j += 1) { - const opt = case.options[j]; + for (let j = 0z; j < len(item.options); j += 1) { + const opt = item.options[j]; z += expr(out, indent, *opt)?; - if (j + 1 < len(case.options)) { + if (j + 1 < len(item.options)) { z += fmt::fprint(out, ", ")?; }; }; - z += fmt::fprint(out, " => ")?; + z += fmt::fprint(out, " =>")?; + }; + for (let j = 0z; j < len(item.exprs); j += 1) { + z += newline(out, indent + 1)?; + z += expr(out, indent + 1, *item.exprs[j])?; + z += fmt::fprint(out, ";")?; }; - z += expr(out, indent, *case.value)?; - z += fmt::fprint(out, ",")?; }; indent -= 1; @@ -517,31 +547,33 @@ fn match_expr( let z = fmt::fprint(out, "match (")?; z += expr(out, indent, *e.value)?; z += fmt::fprint(out, ") {")?; - indent += 1; for (let i = 0z; i < len(e.cases); i += 1) { z += newline(out, indent)?; - const case = e.cases[i]; - if (len(case.name) > 0) { - z += fmt::fprintf(out, "{}: ", case.name)?; + z += fmt::fprint(out, "case ")?; + const item = e.cases[i]; + if (len(item.name) > 0) { + z += fmt::fprintf(out, "{}: ", item.name)?; + }; + z += _type(out, indent, *item._type)?; + z += fmt::fprint(out, " =>")?; + for (let i = 0z; i < len(item.exprs); i += 1) { + z += newline(out, indent + 1)?; + z += expr(out, indent + 1, *item.exprs[i])?; + z += fmt::fprint(out, ";")?; }; - z += _type(out, indent, *case._type)?; - z += fmt::fprint(out, " => ")?; - z += expr(out, indent, *case.value)?; - z += fmt::fprint(out, ",")?; }; - match (e.default) { - e: *ast::expr => { - z += newline(out, indent)?; - z += fmt::fprint(out, "* => ")?; - z += expr(out, indent, *e)?; - z += fmt::fprint(out, ",")?; - }, - null => void, + if (len(e.default) != 0) { + z += newline(out, indent)?; + z += fmt::fprint(out, "case =>")?; + for (let i = 0z; i < len(e.default); i += 1) { + z += newline(out, indent + 1)?; + z += expr(out, indent + 1, *e.default[i])?; + z += fmt::fprint(out, ";")?; + }; }; - indent -= 1; z += newline(out, indent)?; z += fmt::fprint(out, "}")?; return z; diff --git a/hare/unparse/import.ha b/hare/unparse/import.ha @@ -8,21 +8,20 @@ export fn import(out: *io::stream, i: ast::import) (size | io::error) = { let n = 0z; n += fmt::fprint(out, "use ")?; match (i) { - m: ast::import_module => n += ident(out, m)?, - a: ast::import_alias => { - n += fmt::fprint(out, a.alias, "= ")?; - n += ident(out, a.ident)?; - }, - o: ast::import_objects => { - n += ident(out, o.ident)?; - n += fmt::fprint(out, "::{")?; - for (let i = 0z; i < len(o.objects); i += 1) { - n += fmt::fprintf(out, "{}{}", o.objects[i], - if (i + 1 < len(o.objects)) ", " - else "")?; - }; - n += fmt::fprint(out, "}")?; - }, + case m: ast::import_module => + n += ident(out, m)?; + case a: ast::import_alias => + n += fmt::fprint(out, a.alias, "= ")?; + n += ident(out, a.ident)?; + case o: ast::import_objects => + n += ident(out, o.ident)?; + n += fmt::fprint(out, "::{")?; + for (let i = 0z; i < len(o.objects); i += 1) { + n += fmt::fprintf(out, "{}{}", o.objects[i], + if (i + 1 < len(o.objects)) ", " + else "")?; + }; + n += fmt::fprint(out, "}")?; }; n += fmt::fprint(out, ";")?; return n; diff --git a/hare/unparse/type.ha b/hare/unparse/type.ha @@ -5,28 +5,48 @@ use hare::lex; use strio; fn builtin_type(b: ast::builtin_type) str = switch (b) { - ast::builtin_type::BOOL => "bool", - ast::builtin_type::CHAR => "char", - ast::builtin_type::F32 => "f32", - ast::builtin_type::F64 => "f64", - ast::builtin_type::FCONST => abort("FCONST has no lexical representation"), - ast::builtin_type::I16 => "i16", - ast::builtin_type::I32 => "i32", - ast::builtin_type::I64 => "i64", - ast::builtin_type::I8 => "i8", - ast::builtin_type::ICONST => abort("ICONST has no lexical representation"), - ast::builtin_type::INT => "int", - ast::builtin_type::NULL => "null", - ast::builtin_type::RUNE => "rune", - ast::builtin_type::SIZE => "size", - ast::builtin_type::STR => "str", - ast::builtin_type::U16 => "u16", - ast::builtin_type::U32 => "u32", - ast::builtin_type::U64 => "u64", - ast::builtin_type::U8 => "u8", - ast::builtin_type::UINT => "uint", - ast::builtin_type::UINTPTR => "uintptr", - ast::builtin_type::VOID => "void", +case ast::builtin_type::FCONST, ast::builtin_type::ICONST => + abort("ICONST and FCONST have no lexical representation"); +case ast::builtin_type::BOOL => + yield "bool"; +case ast::builtin_type::CHAR => + yield "char"; +case ast::builtin_type::F32 => + yield "f32"; +case ast::builtin_type::F64 => + yield "f64"; +case ast::builtin_type::I16 => + yield "i16"; +case ast::builtin_type::I32 => + yield "i32"; +case ast::builtin_type::I64 => + yield "i64"; +case ast::builtin_type::I8 => + yield "i8"; +case ast::builtin_type::INT => + yield "int"; +case ast::builtin_type::NULL => + yield "null"; +case ast::builtin_type::RUNE => + yield "rune"; +case ast::builtin_type::SIZE => + yield "size"; +case ast::builtin_type::STR => + yield "str"; +case ast::builtin_type::U16 => + yield "u16"; +case ast::builtin_type::U32 => + yield "u32"; +case ast::builtin_type::U64 => + yield "u64"; +case ast::builtin_type::U8 => + yield "u8"; +case ast::builtin_type::UINT => + yield "uint"; +case ast::builtin_type::UINTPTR => + yield "uintptr"; +case ast::builtin_type::VOID => + yield "void"; }; fn prototype( @@ -64,34 +84,34 @@ fn struct_union_type( ) (size | io::error) = { let z = 0z; let membs = match (t.repr) { - st: ast::struct_type => { - z += fmt::fprint(out, "struct {")?; - yield st: []ast::struct_member; - }, - ut: ast::union_type => { - z += fmt::fprint(out, "union {")?; - yield ut: []ast::struct_member; - }, + case st: ast::struct_type => + z += fmt::fprint(out, "struct {")?; + yield st: []ast::struct_member; + case ut: ast::union_type => + z += fmt::fprint(out, "union {")?; + yield ut: []ast::struct_member; }; indent += 1z; for (let i = 0z; i < len(membs); i += 1) { z += newline(out, indent)?; - z += match (membs[i]._offset) { - null => 0z, - ex: *ast::expr => fmt::fprint(out, "@offset(")? - + expr(out, indent, *ex)? - + fmt::fprint(out, ") ")?, + match (membs[i]._offset) { + case null => void; + case ex: *ast::expr => + z += fmt::fprint(out, "@offset(")?; + z += expr(out, indent, *ex)?; + z += fmt::fprint(out, ") ")?; }; - z += match (membs[i].member) { - se: ast::struct_embedded => _type(out, indent, *se)?, - sa: ast::struct_alias => ident(out, sa)?, - sf: ast::struct_field => { - yield fmt::fprintf(out, "{}: ", sf.name)? - + _type(out, indent, *sf._type)?; - }, + match (membs[i].member) { + case se: ast::struct_embedded => + z += _type(out, indent, *se)?; + case sa: ast::struct_alias => + z += ident(out, sa)?; + case sf: ast::struct_field => + z += fmt::fprintf(out, "{}: ", sf.name)?; + z += _type(out, indent, *sf._type)?; }; z += fmt::fprint(out, ",")?; @@ -118,85 +138,83 @@ export fn _type( n += fmt::fprint(out, "!")?; }; match (t.repr) { - a: ast::alias_type => { - if (a.unwrap) { - n += fmt::fprint(out, "...")?; - }; - n += ident(out, a.ident)?; - }, - b: ast::builtin_type => n += fmt::fprint(out, builtin_type(b))?, - e: ast::enum_type => { - if (e.storage != ast::builtin_type::INT) { - n += fmt::fprint(out, "enum", - builtin_type(e.storage), "{")?; - } else { - n += fmt::fprint(out, "enum {")?; - }; - indent += 1; - for (let i = 0z; i < len(e.values); i += 1) { - n += newline(out, indent)?; - let value = e.values[i]; - n += fmt::fprint(out, value.name)?; - match (value.value) { - null => void, - e: *ast::expr => { - n += fmt::fprint(out, " = ")?; - n += expr(out, indent, *e)?; - }, - }; - n += fmt::fprint(out, ",")?; - }; - indent -= 1; + case a: ast::alias_type => + if (a.unwrap) { + n += fmt::fprint(out, "...")?; + }; + n += ident(out, a.ident)?; + case b: ast::builtin_type => + n += fmt::fprint(out, builtin_type(b))?; + case e: ast::enum_type => + if (e.storage != ast::builtin_type::INT) { + n += fmt::fprint(out, "enum", + builtin_type(e.storage), "{")?; + } else { + n += fmt::fprint(out, "enum {")?; + }; + indent += 1; + for (let i = 0z; i < len(e.values); i += 1) { n += newline(out, indent)?; - n += fmt::fprint(out, "}")?; - }, - f: ast::func_type => { - if (f.attrs & ast::func_attrs::NORETURN != 0) { - n += fmt::fprint(out, "@noreturn ")?; - }; - n += fmt::fprint(out, "fn")?; - n += prototype(out, indent, f)?; - }, - l: ast::list_type => { - n += fmt::fprint(out, "[")?; - n += match (l.length) { - ast::len_slice => 0, - ast::len_unbounded => fmt::fprint(out, "*")?, - ast::len_contextual => fmt::fprint(out, "_")?, - e: *ast::expr => expr(out, indent, *e)?, - }; - n += fmt::fprint(out, "]")?; - n += _type(out, indent, *l.members)?; - }, - p: ast::pointer_type => { - if (p.flags & ast::pointer_flags::NULLABLE != 0) { - n += fmt::fprint(out, "nullable ")?; + let value = e.values[i]; + n += fmt::fprint(out, value.name)?; + match (value.value) { + case null => void; + case e: *ast::expr => + n += fmt::fprint(out, " = ")?; + n += expr(out, indent, *e)?; }; + n += fmt::fprint(out, ",")?; + }; + indent -= 1; + n += newline(out, indent)?; + n += fmt::fprint(out, "}")?; + case f: ast::func_type => + if (f.attrs & ast::func_attrs::NORETURN != 0) { + n += fmt::fprint(out, "@noreturn ")?; + }; + n += fmt::fprint(out, "fn")?; + n += prototype(out, indent, f)?; + case l: ast::list_type => + n += fmt::fprint(out, "[")?; + match (l.length) { + case ast::len_slice => void; + case ast::len_unbounded => n += fmt::fprint(out, "*")?; - n += _type(out, indent, *p.referent)?; - }, - ast::struct_type => n += struct_union_type(out, indent, t)?, - ast::union_type => n += struct_union_type(out, indent, t)?, - t: ast::tagged_type => { - n += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - n += _type(out, indent, *t[i])?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, " | ")?; - }; + case ast::len_contextual => + n += fmt::fprint(out, "_")?; + case e: *ast::expr => + n += expr(out, indent, *e)?; + }; + n += fmt::fprint(out, "]")?; + n += _type(out, indent, *l.members)?; + case p: ast::pointer_type => + if (p.flags & ast::pointer_flags::NULLABLE != 0) { + n += fmt::fprint(out, "nullable ")?; + }; + n += fmt::fprint(out, "*")?; + n += _type(out, indent, *p.referent)?; + case ast::struct_type => + n += struct_union_type(out, indent, t)?; + case ast::union_type => + n += struct_union_type(out, indent, t)?; + case t: ast::tagged_type => + n += fmt::fprint(out, "(")?; + for (let i = 0z; i < len(t); i += 1) { + n += _type(out, indent, *t[i])?; + if (i + 1 < len(t)) { + n += fmt::fprint(out, " | ")?; }; - n += fmt::fprint(out, ")")?; - }, - t: ast::tuple_type => { - n += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - n += _type(out, indent, *t[i])?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, ", ")?; - }; + }; + n += fmt::fprint(out, ")")?; + case t: ast::tuple_type => + n += fmt::fprint(out, "(")?; + for (let i = 0z; i < len(t); i += 1) { + n += _type(out, indent, *t[i])?; + if (i + 1 < len(t)) { + n += fmt::fprint(out, ", ")?; }; - n += fmt::fprint(out, ")")?; - }, + }; + n += fmt::fprint(out, ")")?; }; return n; }; diff --git a/io/+linux/file.ha b/io/+linux/file.ha @@ -62,8 +62,10 @@ export fn fd(f: *file) int = f.fd; export fn unwrapfd(s: *stream) (int | void) = { for (!is_file(s)) { s = match (io::source(s)) { - errors::unsupported => return, - s: *io::stream => s, + case errors::unsupported => + return; + case s: *io::stream => + yield s; }; }; return fd(s: *file); @@ -71,20 +73,26 @@ export fn unwrapfd(s: *stream) (int | void) = { fn fd_read(s: *stream, buf: []u8) (size | EOF | error) = { let stream = s: *file; - return match (rt::read(stream.fd, buf: *[*]u8, len(buf))) { - err: rt::errno => errors::errno(err), - n: size => switch (n) { - 0 => EOF, - * => n, - }, + match (rt::read(stream.fd, buf: *[*]u8, len(buf))) { + case err: rt::errno => + return errors::errno(err); + case n: size => + switch (n) { + case 0 => + return EOF; + case => + return n; + }; }; }; fn fd_write(s: *stream, buf: const []u8) (size | error) = { let stream = s: *file; - return match (rt::write(stream.fd, buf: *const [*]u8, len(buf))) { - err: rt::errno => errors::errno(err), - n: size => n, + match (rt::write(stream.fd, buf: *const [*]u8, len(buf))) { + case err: rt::errno => + return errors::errno(err); + case n: size => + return n; }; }; @@ -110,19 +118,23 @@ fn fd_copy(to: *stream, from: *stream) (size | error) = { for (true) { let n = match (rt::sendfile(to.fd, from.fd, null, SENDFILE_MAX)) { - err: rt::errno => switch (err) { - rt::EINVAL => { + case err: rt::errno => + switch (err) { + case rt::EINVAL => if (sum == 0) { return errors::unsupported; }; return errors::errno(err); - }, - * => return errors::errno(err), - }, - n: size => switch (n) { - 0 => return sum, - * => n, - }, + case => + return errors::errno(err); + }; + case n: size => + yield switch (n) { + case 0 => + break; + case => + yield n; + }; }; sum += n; }; @@ -135,8 +147,10 @@ fn fd_seek( whence: whence, ) (off | error) = { let stream = s: *file; - return match (rt::lseek(stream.fd, off: i64, whence: uint)) { - err: rt::errno => errors::errno(err), - n: i64 => n: off, + match (rt::lseek(stream.fd, off: i64, whence: uint)) { + case err: rt::errno => + return errors::errno(err); + case n: i64 => + return n: off; }; }; diff --git a/io/+test/copy.ha b/io/+test/copy.ha @@ -35,12 +35,12 @@ fn strconv::ztos(z: size) str; @test fn copy() void = { let a = test_copier_open(), b = test_copier_open(); match (copy(&b, &a)) { - n: size => { - assert(n == 42); - assert(a.r == 42); - assert(b.w == 42); - }, - error => abort(), + case n: size => + assert(n == 42); + assert(a.r == 42); + assert(b.w == 42); + case error => + abort(); }; close(&a: *stream); close(&b: *stream); @@ -50,11 +50,11 @@ fn strconv::ztos(z: size) str; a.copier = &test_copier_copy; b.copier = &test_copier_copy; match (copy(&b, &a)) { - n: size => { - assert(n == 1337); - assert(b.w == 62893); - }, - error => abort(), + case n: size => + assert(n == 1337); + assert(b.w == 62893); + case error => + abort(); }; close(&a: *stream); close(&b: *stream); @@ -65,12 +65,12 @@ fn strconv::ztos(z: size) str; a.copier = &test_copy_unsupported; b.copier = &test_copy_unsupported; match (copy(&b, &a)) { - n: size => { - assert(n == 42); - assert(a.r == 42); - assert(b.w == 42); - }, - error => abort(), + case n: size => + assert(n == 42); + assert(a.r == 42); + assert(b.w == 42); + case error => + abort(); }; // Fallback (+short writes) @@ -80,13 +80,13 @@ fn strconv::ztos(z: size) str; b.copier = &test_copy_unsupported; b.writer = &teststream_write_short; match (copy(&b, &a)) { - n: size => { - assert(n == 42); - assert(a.r == 42); - assert(a.nreads == 1); - assert(b.w == 42); - assert(b.nwrites > 1); - }, - error => abort(), + case n: size => + assert(n == 42); + assert(a.r == 42); + assert(a.nreads == 1); + assert(b.w == 42); + assert(b.nwrites > 1); + case error => + abort(); }; }; diff --git a/io/+test/limit.ha b/io/+test/limit.ha @@ -6,29 +6,39 @@ use errors; let rlimit = limitreader(&source, 20); match (write(&rlimit, buf)) { - errors::unsupported => void, - * => abort(), + case errors::unsupported => void; + case => + abort(); }; match (read(&rlimit, buf)) { - n: size => assert(n == 15), - error => abort(), + case n: size => + assert(n == 15); + case error => + abort(); }; match (read(&rlimit, buf)) { - n: size => assert(n == 5), - error => abort(), + case n: size => + assert(n == 5); + case error => + abort(); }; let wlimit = limitwriter(&source, 20); match (read(&wlimit, buf)) { - errors::unsupported => void, - * => abort(), + case errors::unsupported => void; + case => + abort(); }; match (write(&wlimit, buf)) { - n: size => assert(n == 15), - error => abort(), + case n: size => + assert(n == 15); + case error => + abort(); }; match (write(&wlimit, buf)) { - n: size => assert(n == 5), - error => abort(), + case n: size => + assert(n == 5); + case error => + abort(); }; }; diff --git a/io/copy.ha b/io/copy.ha @@ -4,26 +4,32 @@ use errors; // return if the source stream is infinite. export fn copy(dest: *stream, src: *stream) (error | size) = { match (dest.copier) { - null => void, - c: *copier => match (c(dest, src)) { - err: error => match (err) { - errors::unsupported => void, // Use fallback - * => return err, - }, - s: size => return s, - }, + case null => void; + case c: *copier => + match (c(dest, src)) { + case err: error => + match (err) { + case errors::unsupported => void; // Use fallback + case => + return err; + }; + case s: size => + return s; + }; }; let w = 0z; static let buf: [4096]u8 = [0...]; for (true) { match (read(src, buf[..])?) { - n: size => for (let i = 0z; i < n) { + case n: size => + for (let i = 0z; i < n) { let r = write(dest, buf[i..n])?; w += r; i += r; - }, - EOF => break, + }; + case EOF => + break; }; }; return w; diff --git a/io/drain.ha b/io/drain.ha @@ -4,8 +4,10 @@ export fn drain(in: *io::stream) ([]u8 | io::error) = { static let buf: [4096]u8 = [0...]; for (true) { match (read(in, buf[..])?) { - n: size => append(sink, buf[..n]...), - EOF => break, + case n: size => + append(sink, buf[..n]...); + case EOF => + break; }; }; return sink; diff --git a/io/stream.ha b/io/stream.ha @@ -36,50 +36,61 @@ export type stream = struct { // Reads up to len(buf) bytes from the reader into the given buffer, returning // the number of bytes read. export fn read(s: *stream, buf: []u8) (size | EOF | error) = { - return match (s.reader) { - null => errors::unsupported, - r: *reader => r(s, buf), + match (s.reader) { + case null => + return errors::unsupported; + case r: *reader => + return r(s, buf); }; }; // Writes up to len(buf) bytes to the stream from the given buffer, returning // the number of bytes written. export fn write(s: *stream, buf: const []u8) (size | error) = { - return match (s.writer) { - null => errors::unsupported, - w: *writer => w(s, buf), + match (s.writer) { + case null => + return errors::unsupported; + case w: *writer => + return w(s, buf); }; }; // Closes the stream. export fn close(s: *stream) void = { - return match (s.closer) { - null => void, - c: *closer => c(s), + match (s.closer) { + case null => void; + case c: *closer => + c(s); }; }; // Sets the offset within the stream. export fn seek(s: *stream, off: off, w: whence) (off | error) = { - return match (s.seeker) { - null => errors::unsupported, - sk: *seeker => sk(s, off, w), + match (s.seeker) { + case null => + return errors::unsupported; + case sk: *seeker => + return sk(s, off, w); }; }; // Returns the current offset within the stream. export fn tell(s: *stream) (off | error) = { - return match (s.seeker) { - null => errors::unsupported, - sk: *seeker => sk(s, 0, whence::CUR), + match (s.seeker) { + case null => + return errors::unsupported; + case sk: *seeker => + return sk(s, 0, whence::CUR); }; }; // Returns the underlying stream for a stream which wraps another stream. export fn source(s: *stream) (*io::stream | errors::unsupported) = { - return match (s.unwrap) { - null => errors::unsupported, - uw: *unwrap => uw(s), + match (s.unwrap) { + case null => + return errors::unsupported; + case uw: *unwrap => + return uw(s); }; }; diff --git a/io/tee.ha b/io/tee.ha @@ -19,10 +19,11 @@ export fn tee(source: *stream, sink: *stream) teestream = { fn tee_read(s: *stream, buf: []u8) (size | EOF | error) = { let s = s: *teestream; - let z = match (read(s.source, buf)) { - err: error => return err, - EOF => return EOF, - z: size => z, + let z = match (read(s.source, buf)?) { + case EOF => + return EOF; + case z: size => + yield z; }; for (let n = 0z; n < z) { n += write(s.sink, buf[n..z])?; diff --git a/linux/io_uring/cqe.ha b/linux/io_uring/cqe.ha @@ -13,12 +13,12 @@ export fn cqe_seen(ring: *io_uring, cqe: *cqe) void = cq_advance(ring, 1); // Waits until a CQE is available, then returns it. The caller must pass the // returned CQE to [[cqe_seen]] to advance the queue. export fn wait(ring: *io_uring) (*cqe | error) = { - return match (get_cqe(ring, 0, 1)) { - err: error => err, - cq: nullable *cqe => { - assert(cq != null); // XXX: Correct? - yield cq: *cqe; - }, + match (get_cqe(ring, 0, 1)) { + case err: error => + return err; + case cq: nullable *cqe => + assert(cq != null); // XXX: Correct? + return cq: *cqe; }; }; @@ -82,9 +82,11 @@ fn get_cqe( }; match (rt::io_uring_enter(ring.fd, - submit, wait, flags: uint, null)) { - err: rt::errno => return errors::errno(err), - n: uint => submit -= n, + submit, wait, flags: uint, null)) { + case err: rt::errno => + return errors::errno(err); + case n: uint => + submit -= n; }; }; return cq; diff --git a/linux/io_uring/queue.ha b/linux/io_uring/queue.ha @@ -18,9 +18,13 @@ export fn get_sqe(ring: *io_uring) nullable *sqe = { // Returns the next available [[sqe]] for this [[io_uring]], or aborts the // program if the queue is full. -export fn must_get_sqe(ring: *io_uring) *sqe = match (get_sqe(ring)) { - null => abort("I/O queue full"), - sq: *sqe => sq, +export fn must_get_sqe(ring: *io_uring) *sqe = { + match (get_sqe(ring)) { + case null => + abort("I/O queue full"); + case sq: *sqe => + return sq; + }; }; fn needs_enter(ring: *io_uring, flags: *enter_flags) bool = { @@ -79,10 +83,12 @@ fn do_submit( ) (uint | error) = { let flags: enter_flags = enter_flags::GETEVENTS; if (needs_enter(ring, &flags) || wait != 0) { - return match (rt::io_uring_enter(ring.fd, + match (rt::io_uring_enter(ring.fd, submitted, wait, flags, null)) { - err: rt::errno => errors::errno(err), - n: uint => n, + case err: rt::errno => + return errors::errno(err); + case n: uint => + return n; }; } else { return submitted; diff --git a/linux/io_uring/register.ha b/linux/io_uring/register.ha @@ -9,19 +9,21 @@ use types; // rt::mmap). export fn register_buffers(ring: *io_uring, iov: []rt::iovec) (void | error) = { assert(len(iov) <= types::UINT_MAX); - return match (rt::io_uring_register(ring.fd, regop::REGISTER_BUFFERS, + match (rt::io_uring_register(ring.fd, regop::REGISTER_BUFFERS, iov: *[*]rt::iovec, len(iov): uint)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; // Unregisters all fixed buffers associated with an [[io_uring]]. export fn unregister_buffers(ring: *io_uring) (void | error) = { - return match (rt::io_uring_register(ring.fd, + match (rt::io_uring_register(ring.fd, regop::UNREGISTER_BUFFERS, null, 0)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; @@ -30,10 +32,11 @@ export fn unregister_buffers(ring: *io_uring) (void | error) = { // [[register_files_update]]. export fn register_files(ring: *io_uring, files: []int) (void | error) = { assert(len(files) <= types::UINT_MAX); - return match (rt::io_uring_register(ring.fd, regop::REGISTER_FILES, + match (rt::io_uring_register(ring.fd, regop::REGISTER_FILES, files: *[*]int, len(files): uint)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; @@ -44,48 +47,53 @@ export fn register_files_update( updates: []files_update, ) (void | error) = { assert(len(updates) <= types::UINT_MAX); - return match (rt::io_uring_register(ring.fd, regop::REGISTER_FILES_UPDATE, + match (rt::io_uring_register(ring.fd, regop::REGISTER_FILES_UPDATE, updates: *[*]files_update, len(updates): uint)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; // Unregisters all files associated with an [[io_uring]]. export fn unregister_files(ring: *io_uring) (void | error) = { - return match (rt::io_uring_register(ring.fd, + match (rt::io_uring_register(ring.fd, regop::UNREGISTER_FILES, null, 0)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; // Registers an eventfd(2) with this [[io_uring]] to be notified of completion // events. export fn register_eventfd(ring: *io_uring, fd: int) (void | error) = { - return match (rt::io_uring_register(ring.fd, + match (rt::io_uring_register(ring.fd, regop::REGISTER_EVENTFD, &fd, 1)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; // Similar to [[register_eventfd]], but only notifies of events which complet // asyncronously. export fn register_eventfd_async(ring: *io_uring, fd: int) (void | error) = { - return match (rt::io_uring_register(ring.fd, + match (rt::io_uring_register(ring.fd, regop::REGISTER_EVENTFD_ASYNC, &fd, 1)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; // Unregisters the eventfd(2) associated with this [[io_uring]]. export fn unregister_eventfd(ring: *io_uring) (void | error) = { - return match (rt::io_uring_register(ring.fd, + match (rt::io_uring_register(ring.fd, regop::UNREGISTER_EVENTFD, null, 0)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; @@ -104,20 +112,23 @@ export fn unregister_eventfd(ring: *io_uring) (void | error) = { // [[io_uring]], returning an ID. Set the personality field of an [[sqe]] to use // that personality for an I/O submission. export fn register_personality(ring: *io_uring) int = { - return match (rt::io_uring_register(ring.fd, + match (rt::io_uring_register(ring.fd, regop::REGISTER_PERSONALITY, null, 0)) { - rt::errno => abort("Unexpected io_uring REGISTER_PERSONALITY error"), - i: int => i, + case rt::errno => + abort("Unexpected io_uring REGISTER_PERSONALITY error"); + case i: int => + return i; }; }; // Unregisters a personality previously configured with // [[register_personality]]. export fn unregister_personality(ring: *io_uring, id: int) (void | error) = { - return match (rt::io_uring_register(ring.fd, + match (rt::io_uring_register(ring.fd, regop::UNREGISTER_PERSONALITY, null, id: uint)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; @@ -125,10 +136,11 @@ export fn unregister_personality(ring: *io_uring, id: int) (void | error) = { // state via [[setup_flags::R_DISABLED]]. Future access to this io_uring is // subject to any configured restrictions. export fn register_enable_rings(ring: *io_uring) (void | error) = { - return match (rt::io_uring_register(ring.fd, + match (rt::io_uring_register(ring.fd, regop::REGISTER_ENABLE_RINGS, null, 0)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; @@ -138,9 +150,10 @@ export fn register_enable_rings(ring: *io_uring) (void | error) = { // [[setup_flags::R_DISABLED]]. export fn register_restrictions(ring: *io_uring, res: []restriction) (void | error) = { assert(len(res) < types::UINT_MAX); - return match (rt::io_uring_register(ring.fd, regop::REGISTER_RESTRICTIONS, + match (rt::io_uring_register(ring.fd, regop::REGISTER_RESTRICTIONS, res: *[*]restriction, len(res): uint)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; diff --git a/linux/io_uring/setup.ha b/linux/io_uring/setup.ha @@ -6,8 +6,10 @@ use rt; // fields are initialized by the kernel. export fn setup(entries: u32, params: *params) (io_uring | error) = { const fd = match (rt::io_uring_setup(entries, params)) { - err: rt::errno => return errors::errno(err), - fd: int => fd, + case err: rt::errno => + return errors::errno(err); + case fd: int => + yield fd; }; let uring = io_uring { @@ -34,8 +36,10 @@ export fn setup(entries: u32, params: *params) (io_uring | error) = { rt::PROT_READ | rt::PROT_WRITE, rt::MAP_SHARED | rt::MAP_POPULATE, fd, OFF_SQ_RING)) { - err: rt::errno => return errors::errno(err), - ptr: *void => ptr, + case err: rt::errno => + return errors::errno(err); + case ptr: *void => + yield ptr; }; cq.ring_ptr = if (uring.features & features::SINGLE_MMAP == features::SINGLE_MMAP) { @@ -44,8 +48,10 @@ export fn setup(entries: u32, params: *params) (io_uring | error) = { rt::PROT_READ | rt::PROT_WRITE, rt::MAP_SHARED | rt::MAP_POPULATE, fd, OFF_CQ_RING)) { - err: rt::errno => return errors::errno(err), - ptr: *void => ptr, + case err: rt::errno => + return errors::errno(err); + case ptr: *void => + yield ptr; }; const ring_ptr = sq.ring_ptr: uintptr; @@ -61,8 +67,10 @@ export fn setup(entries: u32, params: *params) (io_uring | error) = { rt::PROT_READ | rt::PROT_WRITE, rt::MAP_SHARED | rt::MAP_POPULATE, fd, OFF_SQES)) { - err: rt::errno => return errors::errno(err), - ptr: *void => ptr: *[*]sqe, + case err: rt::errno => + return errors::errno(err); + case ptr: *void => + yield ptr: *[*]sqe; }; const ring_ptr = cq.ring_ptr: uintptr; diff --git a/linux/signalfd/signalfd.ha b/linux/signalfd/signalfd.ha @@ -36,20 +36,22 @@ export fn signalfd( mask: *const rt::sigset, flags: int, ) (int | errors::error) = { - return match (rt::signalfd(fd, mask, flags)) { - fd: int => fd, - err: rt::errno => errors::errno(err), + match (rt::signalfd(fd, mask, flags)) { + case fd: int => + return fd; + case err: rt::errno => + return errors::errno(err); }; }; // Reads pending signal info from a signalfd. export fn readsignal(fd: int) (siginfo | errors::error) = { let si = siginfo { ... }; - return match (rt::read(fd, &si, size(siginfo))) { - err: rt::errno => errors::errno(err), - z: size => { - assert(z == size(siginfo)); - return si; - }, + match (rt::read(fd, &si, size(siginfo))) { + case err: rt::errno => + return errors::errno(err); + case z: size => + assert(z == size(siginfo)); + return si; }; }; diff --git a/linux/vdso/vdso.ha b/linux/vdso/vdso.ha @@ -39,8 +39,10 @@ fn get_vdso_ctx() nullable *vdso_ctx = { vdso_checked = true; const eh = match (sys_infoehdr()) { - null => return null, - x: *elf::header64 => x, + case null => + return null; + case x: *elf::header64 => + yield x; }; const ehui = eh: uintptr; @@ -51,13 +53,13 @@ fn get_vdso_ctx() nullable *vdso_ctx = { for (let i: u16 = 0; i < eh.e_phnum; i += 1) { const ph = phui: *elf::phdr64; switch (ph.p_type) { - elf::pt::LOAD => - baseseg = ehui + - ph.p_offset: uintptr - - ph.p_vaddr: uintptr, - elf::pt::DYNAMIC => - dynvec = ehui + ph.p_offset: uintptr, - * => void, + case elf::pt::LOAD => + baseseg = ehui + + ph.p_offset: uintptr - + ph.p_vaddr: uintptr; + case elf::pt::DYNAMIC => + dynvec = ehui + ph.p_offset: uintptr; + case => void; }; phui += eh.e_phentsize: uintptr; }; @@ -77,12 +79,18 @@ fn get_vdso_ctx() nullable *vdso_ctx = { for (let i = 0; dynv[i].d_tag != elf::dt::NULL; i += 1) { const tabptr = (segbase + dynv[i].d_val: uintptr): *void; switch (dynv[i].d_tag) { - elf::dt::STRTAB => stringtab = tabptr: *char, - elf::dt::SYMTAB => symtab = tabptr: *[*]elf::sym64, - elf::dt::HASH => hashhdr = tabptr: *elf::hashhdr, - elf::dt::VERSYM => versym = tabptr: *[*]u16, - elf::dt::VERDEF => verdef = tabptr: *elf::verdef64, - * => continue, + case elf::dt::STRTAB => + stringtab = tabptr: *char; + case elf::dt::SYMTAB => + symtab = tabptr: *[*]elf::sym64; + case elf::dt::HASH => + hashhdr = tabptr: *elf::hashhdr; + case elf::dt::VERSYM => + versym = tabptr: *[*]u16; + case elf::dt::VERDEF => + verdef = tabptr: *elf::verdef64; + case => + continue; }; }; @@ -111,12 +119,16 @@ fn get_vdso_ctx() nullable *vdso_ctx = { fn vdso_checkver(ctx: *vdso_ctx, version: str, num: u32) bool = { let prev = null: *elf::verdef64; let cur = match (ctx.verdef) { - null => return true, - vd: *elf::verdef64 => vd, + case null => + return true; + case vd: *elf::verdef64 => + yield vd; }; const versym = match (ctx.versym) { - null => return true, - vs: *[*]u16 => vs[num] & 0x7fff + case null => + return true; + case vs: *[*]u16 => + yield vs[num] & 0x7ff; }; for (cur != prev) { if (cur.vd_flags & elf::ver_flg::BASE: u16 == 0 && @@ -135,8 +147,10 @@ fn vdso_checkver(ctx: *vdso_ctx, version: str, num: u32) bool = { export fn getsym(symname: str, symver: str) nullable *void = { const ctx = match (get_vdso_ctx()) { - null => return null, - x: *vdso_ctx => x, + case null => + return null; + case x: *vdso_ctx => + yield x; }; const sym_types = (1 << elf::stt::NOTYPE | diff --git a/math/floats.ha b/math/floats.ha @@ -187,9 +187,11 @@ export fn isneginf(n: f64) bool = { // Returns true if the given floating-point number is normal. export fn isnormal(n: types::floating) bool = { - return match (n) { - n: f32 => isnormalf32(n), - n: f64 => isnormalf64(n), + match (n) { + case n: f32 => + return isnormalf32(n); + case n: f64 => + return isnormalf64(n); }; }; @@ -211,9 +213,11 @@ export fn isnormalf32(n: f32) bool = { // Returns true if the given floating-point number is subnormal. export fn issubnormal(n: types::floating) bool = { - return match (n) { - n: f32 => issubnormalf32(n), - n: f64 => issubnormalf64(n), + match (n) { + case n: f32 => + return issubnormalf32(n); + case n: f64 => + return issubnormalf64(n); }; }; @@ -295,9 +299,11 @@ export fn absf32(n: f32) f32 = { // Returns the absolute value of floating-point number n. export fn absf(n: types::floating) f64 = { - return match (n) { - n: f64 => absf64(n), - n: f32 => (absf32(n): f64), + match (n) { + case n: f64 => + return absf64(n); + case n: f32 => + return (absf32(n): f64); }; }; @@ -338,9 +344,11 @@ export fn signf32(x: f32) i64 = { // Returns 1 if x is positive and -1 if x is negative. Note that zero is also // signed. export fn signf(x: types::floating) i64 = { - return match (x) { - n: f64 => signf64(n), - n: f32 => signf32(n), + match (x) { + case n: f64 => + return signf64(n); + case n: f32 => + return signf32(n); }; }; @@ -368,9 +376,11 @@ export fn ispositivef32(x: f32) bool = signf32(x) == 1i32; // Returns whether or not x is positive. export fn ispositive(x: types::floating) bool = { - return match (x) { - n: f64 => ispositivef64(n), - n: f32 => ispositivef32(n), + match (x) { + case n: f64 => + return ispositivef64(n); + case n: f32 => + return ispositivef32(n); }; }; @@ -382,9 +392,11 @@ export fn isnegativef32(x: f32) bool = signf32(x) == -1i32; // Returns whether or not x is negative. export fn isnegative(x: types::floating) bool = { - return match (x) { - n: f64 => isnegativef64(n), - n: f32 => isnegativef32(n), + match (x) { + case n: f64 => + return isnegativef64(n); + case n: f32 => + return isnegativef32(n); }; }; @@ -402,9 +414,11 @@ export fn copysignf32(x: f32, y: f32) f32 = { // Returns x, but with the sign of y. export fn copysign(x: types::floating, y: types::floating) f64 = { - return match (x) { - n: f64 => copysignf64(n, (y: f64)), - n: f32 => (copysignf32(n, (y: f32)): f64), + match (x) { + case n: f64 => + return copysignf64(n, (y: f64)); + case n: f32 => + return (copysignf32(n, (y: f32)): f64); }; }; @@ -492,9 +506,11 @@ export fn frexpf32(n: f32) (f64, i64) = { // Breaks a float down into its mantissa and exponent. The mantissa will be // between 0.5 and 1. export fn frexp(n: types::floating) (f64, i64) = { - return match (n) { - n: f64 => frexpf64(n), - n: f32 => frexpf32(n), + match (n) { + case n: f64 => + return frexpf64(n); + case n: f32 => + return frexpf32(n); }; }; diff --git a/math/ints.ha b/math/ints.ha @@ -38,11 +38,15 @@ export fn absi64(n: i64) i64 = { // Returns the absolute value of signed integer n. export fn absi(n: types::integer) i64 = { - return match (n) { - n: i8 => (absi8(n): i64), - n: i16 => (absi16(n): i64), - n: i32 => (absi32(n): i64), - n: i64 => (absi64(n): i64), + match (n) { + case n: i8 => + return (absi8(n): i64); + case n: i16 => + return (absi16(n): i64); + case n: i32 => + return (absi32(n): i64); + case n: i64 => + return (absi64(n): i64); }; }; @@ -111,11 +115,15 @@ export fn signi64(n: i64) i64 = { // Return 1 if n is positive, -1 if it's negative and 0 if it's 0. export fn signi(n: types::integer) i64 = { - return match (n) { - n: i8 => (signi8(n): i64), - n: i16 => (signi16(n): i64), - n: i32 => (signi32(n): i64), - n: i64 => (signi64(n): i64), + match (n) { + case n: i8 => + return (signi8(n): i64); + case n: i16 => + return (signi16(n): i64); + case n: i32 => + return (signi32(n): i64); + case n: i64 => + return (signi64(n): i64); }; }; diff --git a/math/math.ha b/math/math.ha @@ -70,9 +70,11 @@ export fn eqwithinf32(x: f32, y: f32, tol: f32) bool = { // Returns whether x and y are within tol of each other. export fn eqwithin(x: types::floating, y: types::floating, tol: types::floating) bool = { - return match (x) { - n: f64 => eqwithinf64(n, y as f64, tol as f64), - n: f32 => eqwithinf32(n, y as f32, tol as f32), + match (x) { + case n: f64 => + return eqwithinf64(n, y as f64, tol as f64); + case n: f32 => + return eqwithinf32(n, y as f32, tol as f32); }; }; @@ -88,9 +90,11 @@ export fn isclosef32(x: f32, y: f32) bool = { // Returns whether x and y are within [[STANDARD_TOL]] of each other. export fn isclose(x: types::floating, y: types::floating) bool = { - return match (x) { - n: f64 => isclosef64(n, y as f64), - n: f32 => isclosef32(n, y as f32), + match (x) { + case n: f64 => + return isclosef64(n, y as f64); + case n: f32 => + return isclosef32(n, y as f32); }; }; diff --git a/net/+linux.ha b/net/+linux.ha @@ -25,8 +25,10 @@ export fn stream_accept(l: *listener) (io::file | error) = { let sn = rt::sockaddr {...}; const sz = size(rt::sockaddr): u32; const fd = match (rt::accept(l.fd, &sn, &sz)) { - err: rt::errno => return errors::errno(err), - fd: int => fd, + case err: rt::errno => + return errors::errno(err); + case fd: int => + yield fd; }; static let namebuf: [32]u8 = [0...]; diff --git a/net/dial/ip.ha b/net/dial/ip.ha @@ -11,10 +11,12 @@ fn dial_tcp(addr: str, service: str) (io::file | error) = { for (let i = 0z; i < len(addrs); i += 1) { const addr = addrs[i]; match (tcp::connect(addr, port)) { - conn: io::file => return conn, - err: net::error => if (i + 1 >= len(addrs)) { + case conn: io::file => + return conn; + case err: net::error => + if (i + 1 >= len(addrs)) { return err; - }, + }; }; }; abort(); // Unreachable @@ -26,10 +28,12 @@ fn dial_udp(addr: str, service: str) (io::file | error) = { for (let i = 0z; i < len(addrs); i += 1) { const addr = addrs[i]; match (udp::connect(addr, port)) { - sock: io::file => return sock, - err: net::error => if (i + 1 >= len(addrs)) { + case sock: io::file => + return sock; + case err: net::error => + if (i + 1 >= len(addrs)) { return err; - }, + }; }; }; abort(); // Unreachable diff --git a/net/dial/registry.ha b/net/dial/registry.ha @@ -17,11 +17,15 @@ export type error = !(invalid_address | unknown_service | net::error | dns::erro // Converts an [[error]] to a human-readable string. export fn strerror(err: error) const str = { // TODO: These could be better - return match (err) { - invalid_address => "Attempted to dial an invalid address", - unknown_service => "Unknown service", - err: net::error => net::strerror(err), - err: dns::error => dns::strerror(err), + match (err) { + case invalid_address => + return "Attempted to dial an invalid address"; + case unknown_service => + return "Unknown service"; + case err: net::error => + return net::strerror(err); + case err: dns::error => + return dns::strerror(err); }; }; diff --git a/net/dial/resolve.ha b/net/dial/resolve.ha @@ -16,18 +16,22 @@ export fn resolve( service: str, ) (([]ip::addr, u16) | error) = { let port = match (strings::index(addr, ':')) { - void => match (strconv::stou16(service)) { - u: u16 => u, - * => 0u16, - }, - i: size => { - const sub = strings::sub(addr, i + 1, strings::end); - addr = strings::sub(addr, 0, i); - yield match (strconv::stou16(sub)) { - u: u16 => u, - * => return invalid_address, - }; - }, + case void => + yield match (strconv::stou16(service)) { + case u: u16 => + yield u; + case => + yield 0u16; + }; + case i: size => + const sub = strings::sub(addr, i + 1, strings::end); + addr = strings::sub(addr, 0, i); + yield match (strconv::stou16(sub)) { + case u: u16 => + yield u; + case => + return invalid_address; + }; }; if (service == "unknown" && port == 0) { return unknown_service; @@ -35,8 +39,9 @@ export fn resolve( let addrs = resolve_addr(addr)?; if (port == 0) match (lookup_service(proto, service)) { - p: u16 => port = p, - void => void, + case p: u16 => + port = p; + case void => void; }; // TODO: @@ -55,13 +60,12 @@ export fn resolve( fn resolve_addr(addr: str) ([]ip::addr | error) = { match (ip::parse(addr)) { - addr: ip::addr => { - // XXX: harec bug prevents just alloc'ing this - let addrs: []ip::addr = []; - append(addrs, addr); - return addrs; - }, - ip::invalid => void, + case addr: ip::addr => + // XXX: harec bug prevents just alloc'ing this + let addrs: []ip::addr = []; + append(addrs, addr); + return addrs; + case ip::invalid => void; }; const addrs = hosts::lookup(addr); @@ -133,11 +137,12 @@ fn resolve_addr(addr: str) ([]ip::addr | error) = { fn collect_answers(addrs: *[]ip::addr, answers: *[]dns::rrecord) void = { for (let i = 0z; i < len(answers); i += 1) { match (answers[i].rdata) { - // XXX: harec bug prevents us from casting directly to - // ip::addr - addr: dns::aaaa => append(addrs, addr: ip::addr6: ip::addr), - addr: dns::a => append(addrs, addr: ip::addr4: ip::addr), - * => void, + // XXX: harec bug prevents us from casting directly to ip::addr + case addr: dns::aaaa => + append(addrs, addr: ip::addr6: ip::addr); + case addr: dns::a => + append(addrs, addr: ip::addr4: ip::addr); + case => void; }; }; }; diff --git a/net/dns/decode.ha b/net/dns/decode.ha @@ -145,16 +145,19 @@ fn decode_rrecord(dec: *decoder) (rrecord | format) = { }; fn decode_rdata(dec: *decoder, rtype: rtype, rlen: size) (rdata | format) = { - return switch (rtype) { - rtype::A => decode_a(dec), - rtype::AAAA => decode_aaaa(dec), - rtype::MX => decode_mx(dec), - rtype::TXT => decode_txt(dec), - * => { - let buf = dec.cur[..rlen]; - dec.cur = dec.cur[rlen..]; - return buf: unknown_rdata; - }, + switch (rtype) { + case rtype::A => + return decode_a(dec); + case rtype::AAAA => + return decode_aaaa(dec); + case rtype::MX => + return decode_mx(dec); + case rtype::TXT => + return decode_txt(dec); + case => + let buf = dec.cur[..rlen]; + dec.cur = dec.cur[rlen..]; + return buf: unknown_rdata; }; }; diff --git a/net/dns/error.ha b/net/dns/error.ha @@ -30,27 +30,42 @@ export type error = !(format | server_failure | name_error export fn strerror(err: error) const str = { static let buf: [64]u8 = [0...]; - return match (err) { - format => "The DNS message was poorly formatted", - server_failure => "The name server was unable to process this query due to a problem with the name server", - name_error => "The domain name referenced in the query does not exist", - not_implemented => "The name server does not support the requested kind of query", - refused => "The name server refuses to perform the specified operation for policy reasons", - ue: unknown_error => fmt::bsprintf(buf, "Unknown DNS error {}", ue: u8), - errors::overflow => "The encoded message would exceed the buffer size", - errors::timeout => "The DNS request timed out", - err: net::error => net::strerror(err), + match (err) { + case format => + return "The DNS message was poorly formatted"; + case server_failure => + return "The name server was unable to process this query due to a problem with the name server"; + case name_error => + return "The domain name referenced in the query does not exist"; + case not_implemented => + return "The name server does not support the requested kind of query"; + case refused => + return "The name server refuses to perform the specified operation for policy reasons"; + case ue: unknown_error => + return fmt::bsprintf(buf, "Unknown DNS error {}", ue: u8); + case errors::overflow => + return "The encoded message would exceed the buffer size"; + case errors::timeout => + return "The DNS request timed out"; + case err: net::error => + return net::strerror(err); }; }; fn check_rcode(rcode: rcode) (void | error) = { - return switch (rcode) { - rcode::NO_ERROR => void, - rcode::FMT_ERROR => format, - rcode::SERVER_FAILURE => server_failure, - rcode::NAME_ERROR => name_error, - rcode::NOT_IMPLEMENTED => not_implemented, - rcode::REFUSED => refused, - * => rcode: unknown_error, + switch (rcode) { + case rcode::NO_ERROR => void; + case rcode::FMT_ERROR => + return format; + case rcode::SERVER_FAILURE => + return server_failure; + case rcode::NAME_ERROR => + return name_error; + case rcode::NOT_IMPLEMENTED => + return not_implemented; + case rcode::REFUSED => + return refused; + case => + return rcode: unknown_error; }; }; diff --git a/net/dns/query.ha b/net/dns/query.ha @@ -49,12 +49,10 @@ export fn query(query: *message, servers: ip::addr...) (*message | error) = { // We send requests in parallel to all configured servers and take the // first one which sends us a reasonable answer. for (let i = 0z; i < len(servers); i += 1) match (servers[i]) { - ip::addr4 => { - udp::sendto(&socket4, sendbuf[..z], servers[i], 53)?; - }, - ip::addr6 => { - udp::sendto(&socket6, sendbuf[..z], servers[i], 53)?; - }, + case ip::addr4 => + udp::sendto(&socket4, sendbuf[..z], servers[i], 53)?; + case ip::addr6 => + udp::sendto(&socket6, sendbuf[..z], servers[i], 53)?; }; let header = header { ... }; diff --git a/net/dns/types.ha b/net/dns/types.ha @@ -194,8 +194,10 @@ fn bytes_free(in: [][]u8) void = { fn rrecord_finish(rr: *rrecord) void = { strings_free(rr.name); match (rr.rdata) { - mx: mx => strings_free(mx.name), - tx: txt => bytes_free(tx: [][]u8), - * => void, + case mx: mx => + strings_free(mx.name); + case tx: txt => + bytes_free(tx: [][]u8); + case => void; }; }; diff --git a/net/errors.ha b/net/errors.ha @@ -8,9 +8,11 @@ export type error = !(unknownproto | ...errors::error); // Converts a [[net::error]] into a human-readable string. export fn strerror(err: error) const str = { - return match (err) { - unknownproto => "Unsupported protocol", - err: errors::error => errors::strerror(err), + match (err) { + case unknownproto => + return "Unsupported protocol"; + case err: errors::error => + return errors::strerror(err); }; }; diff --git a/net/ip/+linux.ha b/net/ip/+linux.ha @@ -2,35 +2,38 @@ use rt; use endian; export fn to_native(a: addr, port: u16) rt::sockaddr = { - return match (a) { - v4: addr4 => rt::sockaddr { in = rt::sockaddr_in { + match (a) { + case v4: addr4 => + return rt::sockaddr { in = rt::sockaddr_in { sin_family = rt::AF_INET, sin_port = endian::htonu16(port), sin_addr = rt::in_addr { s_addr = *(&v4[0]: *void: *u32) }, - } }, - v6: addr6 => rt::sockaddr { in6 = rt::sockaddr_in6 { + } }; + case v6: addr6 => + return rt::sockaddr { in6 = rt::sockaddr_in6 { sin6_family = rt::AF_INET6, sin6_port = endian::htonu16(port), sin6_addr = rt::in6_addr { s6_addr = v6 }, - } }, + } }; }; }; export fn from_native(a: rt::sockaddr) (addr, u16) = { let family = a.in.sin_family; switch (family) { - rt::AF_INET => { - let addr = a.in.sin_addr.s_addr; - return ( - [addr: u8, (addr >> 8): u8, (addr >> 16): u8, - (addr >> 24): u8]: addr4, - endian::ntohu16(a.in.sin_port) - ); - }, - rt::AF_INET6 => return ( + case rt::AF_INET => + let addr = a.in.sin_addr.s_addr; + return ( + [addr: u8, (addr >> 8): u8, (addr >> 16): u8, + (addr >> 24): u8]: addr4, + endian::ntohu16(a.in.sin_port) + ); + case rt::AF_INET6 => + return ( a.in6.sin6_addr.s6_addr: addr6, endian::ntohu16(a.in6.sin6_port) - ), - * => abort("Wrong address family!"), + ); + case => + abort("Wrong address family!"); }; }; diff --git a/net/ip/+test.ha b/net/ip/+test.ha @@ -59,8 +59,10 @@ fn ip_test(s: str, expected: (addr|invalid)) void = { fn subnet_test_simple(s: str) void = { let net = match (parsecidr(s)) { - a: subnet => a, - * => return, + case a: subnet => + yield a; + case => + return; }; let fmted = string(net); assert(fmted == s); diff --git a/net/ip/ip.ha b/net/ip/ip.ha @@ -34,21 +34,19 @@ export type invalid = !void; // Test if two [[addr]]s are equal. export fn equal(l: addr, r: addr) bool = { - return match (l) { - l: addr4 => { - if (!(r is addr4)) { - return false; - }; - let r = r as addr4; - yield bytes::equal(l, r); - }, - l: addr6 => { - if (!(r is addr6)) { - return false; - }; - let r = r as addr6; - yield bytes::equal(l, r); - }, + match (l) { + case l: addr4 => + if (!(r is addr4)) { + return false; + }; + let r = r as addr4; + return bytes::equal(l, r); + case l: addr6 => + if (!(r is addr6)) { + return false; + }; + let r = r as addr6; + return bytes::equal(l, r); }; }; @@ -62,8 +60,10 @@ fn parsev4(st: str) (addr4 | invalid) = { return invalid; }; ret[i] = match (strconv::stou8(s)) { - term: u8 => term, - * => return invalid + case term: u8 => + yield term; + case => + return invalid; }; }; if (i < 4 || !(strings::next_token(&tok) is void)) { @@ -89,8 +89,10 @@ fn parsev6(st: str) (addr6 | invalid) = { let i = 0; for (i < 16) { let s = match (strings::next_token(&tok)) { - s: str => s, - void => break, + case s: str => + yield s; + case void => + break; }; if (s == "") { if (ells != -1) { @@ -137,12 +139,14 @@ fn parsev6(st: str) (addr6 | invalid) = { // Parses an IP address. export fn parse(s: str) (addr | invalid) = { match (parsev4(s)) { - v4: addr4 => return v4, - invalid => void, + case v4: addr4 => + return v4; + case invalid => void; }; match (parsev6(s)) { - v6: addr6 => return v6, - invalid => void, + case v6: addr6 => + return v6; + case invalid => void; }; return invalid; }; @@ -214,8 +218,10 @@ fn fillmask(mask: []u8, val: u8) void = { // Returns an addr representing a netmask fn cidrmask(addr: addr, val: u8) (addr | invalid) = { let a_len: u8 = match (addr) { - addr4 => 4, - addr6 => 16, + case addr4 => + yield 4; + case addr6 => + yield 16; }; if (val > 8 * a_len) @@ -240,8 +246,10 @@ export fn parsecidr(st: str) (subnet | invalid) = { let addr = parse(ips)?; let masks = wanttoken(&tok)?; let val = match (strconv::stou8(masks)) { - x: u8 => x, - * => return invalid, + case x: u8 => + yield x; + case => + return invalid; }; if (!(strings::next_token(&tok) is void)) { return invalid; @@ -278,19 +286,21 @@ fn masklen(addr: []u8) (void | size) = { fn fmtmask(s: *io::stream, mask: addr) (io::error | size) = { let ret = 0z; let slice = match (mask) { - v4: addr4 => v4[..], - v6: addr6 => v6[..], + case v4: addr4 => + yield v4[..]; + case v6: addr6 => + yield v6[..]; }; match (masklen(slice)) { - // format as hex, if zero runs are not contiguous + case void => + // Format as hex, if zero runs are not contiguous // (like golang does) - void => { - for (let i = 0z; i < len(slice); i += 1) { - ret += fmt::fprintf(s, "{:x}", slice[i])?; - }; - }, - // standard CIDR integer - n: size => ret += fmt::fprintf(s, "{}", n)?, + for (let i = 0z; i < len(slice); i += 1) { + ret += fmt::fprintf(s, "{:x}", slice[i])?; + }; + case n: size => + // Standard CIDR integer + ret += fmt::fprintf(s, "{}", n)?; }; return ret; }; @@ -305,10 +315,13 @@ fn fmtsubnet(s: *io::stream, subnet: subnet) (io::error | size) = { // Formats an [[addr]] or [[subnet]] and prints it to a stream. export fn fmt(s: *io::stream, item: (...addr | subnet)) (io::error | size) = { - return match (item) { - v4: addr4 => fmtv4(s, v4)?, - v6: addr6 => fmtv6(s, v6)?, - sub: subnet => fmtsubnet(s, sub), + match (item) { + case v4: addr4 => + return fmtv4(s, v4)?; + case v6: addr6 => + return fmtv6(s, v6)?; + case sub: subnet => + return fmtsubnet(s, sub); }; }; @@ -324,8 +337,10 @@ export fn string(item: (...addr | subnet)) str = { }; fn wanttoken(tok: *strings::tokenizer) (str | invalid) = { - return match (strings::next_token(tok)) { - s: str => s, - void => invalid + match (strings::next_token(tok)) { + case s: str => + return s; + case void => + return invalid; }; }; diff --git a/net/listener.ha b/net/listener.ha @@ -4,16 +4,19 @@ use io; // Accepts the next connection from a listener. Blocks until a new connection is // available. export fn accept(l: *listener) (io::file | error) = { - return match (l.accept) { - f: *fn(l: *listener) (io::file | error) => f(l), - null => errors::unsupported, + match (l.accept) { + case f: *fn(l: *listener) (io::file | error) => + return f(l); + case null => + return errors::unsupported; }; }; // Shuts down a [[listener]] and frees resources associated with it. export fn shutdown(l: *listener) void = { match (l.shutdown) { - f: *fn(l: *listener) void => f(l), - null => void, + case f: *fn(l: *listener) void => + f(l); + case null => void; }; }; diff --git a/net/tcp/+linux.ha b/net/tcp/+linux.ha @@ -13,12 +13,17 @@ export fn connect( options: connect_option... ) (io::file | net::error) = { const sockaddr = ip::to_native(addr, port); - const sockfd = match (rt::socket(match (addr) { - ip::addr4 => rt::AF_INET: int, - ip::addr6 => rt::AF_INET6: int, - }, rt::SOCK_STREAM, 0)) { - err: rt::errno => return errors::errno(err), - fd: int => fd, + const family = match (addr) { + case ip::addr4 => + yield rt::AF_INET: int; + case ip::addr6 => + yield rt::AF_INET6: int; + }; + const sockfd = match (rt::socket(family, rt::SOCK_STREAM, 0)) { + case err: rt::errno => + return errors::errno(err); + case fd: int => + yield fd; }; let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; @@ -29,8 +34,9 @@ export fn connect( }; const sz = size(rt::sockaddr): u32; match (rt::connect(sockfd, &sockaddr, sz)) { - err: rt::errno => return errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; return io::fdopen(sockfd, ip::string(addr), io::mode::READ | io::mode::WRITE); @@ -43,12 +49,17 @@ export fn listen( options: listen_option... ) (*net::listener | net::error) = { const sockaddr = ip::to_native(addr, port); - const sockfd = match (rt::socket(match (addr) { - ip::addr4 => rt::AF_INET: int, - ip::addr6 => rt::AF_INET6: int, - }, rt::SOCK_STREAM, 0)) { - err: rt::errno => return errors::errno(err), - fd: int => fd, + const family = match (addr) { + case ip::addr4 => + yield rt::AF_INET: int; + case ip::addr6 => + yield rt::AF_INET6: int; + }; + const sockfd = match (rt::socket(family, rt::SOCK_STREAM, 0)) { + case err: rt::errno => + return errors::errno(err); + case fd: int => + yield fd; }; let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; @@ -56,33 +67,42 @@ export fn listen( let bk: u32 = 10; for (let i = 0z; i < len(options); i += 1) { match (options[i]) { - reuseaddr => setsockopt(sockfd, rt::SO_REUSEADDR, true)?, - reuseport => setsockopt(sockfd, rt::SO_REUSEPORT, true)?, - keepalive => setsockopt(sockfd, rt::SO_KEEPALIVE, true)?, - b: backlog => bk = b, - p: portassignment => void, + case reuseaddr => + setsockopt(sockfd, rt::SO_REUSEADDR, true)?; + case reuseport => + setsockopt(sockfd, rt::SO_REUSEPORT, true)?; + case keepalive => + setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; + case b: backlog => + bk = b; + case p: portassignment => void; }; }; match (rt::bind(sockfd, &sockaddr, size(rt::sockaddr): u32)) { - err: rt::errno => return errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; - match ((rt::listen(sockfd, bk))) { - err: rt::errno => return errors::errno(err), - int => void, + match (rt::listen(sockfd, bk)) { + case err: rt::errno => + return errors::errno(err); + case int => void; }; for (let i = 0z; i < len(options); i += 1) { let portout = match (options[i]) { - p: portassignment => p, - * => continue, + case p: portassignment => + yield p; + case => + continue; }; let sn = rt::sockaddr {...}; let al = size(rt::sockaddr): u32; match (rt::getsockname(sockfd, &sn, &al)) { - err: rt::errno => return errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; const addr = ip::from_native(sn); *portout = addr.1; @@ -115,9 +135,10 @@ fn setsockopt( value: bool, ) (void | net::error) = { let val: int = if (value) 1 else 0; - return match (rt::setsockopt(sockfd, rt::SOL_SOCKET, option, + match (rt::setsockopt(sockfd, rt::SOL_SOCKET, option, &val: *void, size(int): u32)) { - err: rt::errno => errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; }; diff --git a/net/udp/+linux.ha b/net/udp/+linux.ha @@ -10,21 +10,27 @@ export fn connect( dest: ip::addr, port: u16, ) (io::file | net::error) = { - const sockfd = match (rt::socket(match (dest) { - ip::addr4 => rt::AF_INET: int, - ip::addr6 => rt::AF_INET6: int, - }, rt::SOCK_DGRAM, 0)) { - err: rt::errno => return errors::errno(err), - fd: int => fd, + const family = match (dest) { + case ip::addr4 => + yield rt::AF_INET: int; + case ip::addr6 => + yield rt::AF_INET6: int; + }; + const sockfd = match (rt::socket(family, rt::SOCK_DGRAM, 0)) { + case err: rt::errno => + return errors::errno(err); + case fd: int => + yield fd; }; const sockaddr = ip::to_native(dest, port); const sz = size(rt::sockaddr): u32; - return match (rt::connect(sockfd, &sockaddr, sz)) { - int => io::fdopen(sockfd, - "<udp connection>", - io::mode::READ | io::mode::WRITE), - err: rt::errno => errors::errno(err), + match (rt::connect(sockfd, &sockaddr, sz)) { + case int => + return io::fdopen(sockfd, "<udp connection>", + io::mode::READ | io::mode::WRITE); + case err: rt::errno => + return errors::errno(err); }; }; @@ -34,12 +40,17 @@ export fn listen( port: u16, options: listen_option... ) (io::file | net::error) = { - const sockfd = match (rt::socket(match (addr) { - ip::addr4 => rt::AF_INET: int, - ip::addr6 => rt::AF_INET6: int, - }, rt::SOCK_DGRAM, 0)) { - err: rt::errno => return errors::errno(err), - fd: int => fd, + const family = match (addr) { + case ip::addr4 => + yield rt::AF_INET: int; + case ip::addr6 => + yield rt::AF_INET6: int; + }; + const sockfd = match (rt::socket(family, rt::SOCK_DGRAM, 0)) { + case err: rt::errno => + return errors::errno(err); + case fd: int => + yield fd; }; let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; @@ -47,8 +58,9 @@ export fn listen( const sockaddr = ip::to_native(addr, port); const sz = size(rt::sockaddr): u32; match (rt::bind(sockfd, &sockaddr, sz)) { - int => void, - err: rt::errno => return errors::errno(err), + case int => void; + case err: rt::errno => + return errors::errno(err); }; for (let i = 0z; i < len(options); i += 1) { @@ -56,8 +68,9 @@ export fn listen( let sn = rt::sockaddr {...}; let al = size(rt::sockaddr): u32; match (rt::getsockname(sockfd, &sn, &al)) { - err: rt::errno => return errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; const addr = ip::from_native(sn); *options[i] = addr.1; @@ -69,9 +82,11 @@ export fn listen( // Sends a UDP packet to the destination previously specified by [[connect]]. export fn send(sock: *io::file, buf: []u8) (size | net::error) = { - return match (rt::send(io::fd(sock), buf: *[*]u8, len(buf), 0)) { - sz: size => sz, - err: rt::errno => errors::errno(err), + match (rt::send(io::fd(sock), buf: *[*]u8, len(buf), 0)) { + case sz: size => + return sz; + case err: rt::errno => + return errors::errno(err); }; }; @@ -84,10 +99,12 @@ export fn sendto( ) (size | net::error) = { const sockaddr = ip::to_native(dest, port); const sz = size(rt::sockaddr): u32; - return match (rt::sendto(io::fd(sock), buf: *[*]u8, len(buf), + match (rt::sendto(io::fd(sock), buf: *[*]u8, len(buf), 0, &sockaddr, sz)) { - sz: size => sz, - err: rt::errno => errors::errno(err), + case sz: size => + return sz; + case err: rt::errno => + return errors::errno(err); }; }; @@ -102,19 +119,23 @@ export fn recvfrom( const sockaddr = rt::sockaddr { ... }; const sz = match (rt::recvfrom(io::fd(sock), buf: *[*]u8, len(buf), 0, &sockaddr, &addrsz)) { - sz: size => sz, - err: rt::errno => return errors::errno(err), + case sz: size => + yield sz; + case err: rt::errno => + return errors::errno(err); }; assert(addrsz <= size(rt::sockaddr)); const peer = ip::from_native(sockaddr); match (src) { - null => void, - src: *ip::addr => *src = peer.0, + case null => void; + case src: *ip::addr => + *src = peer.0; }; match (port) { - null => void, - port: *u16 => *port = peer.1, + case null => void; + case port: *u16 => + *port = peer.1; }; return sz; diff --git a/net/unix/+linux.ha b/net/unix/+linux.ha @@ -11,20 +11,25 @@ use types; // established. export fn connect(addr: addr) (io::file | net::error) = { let sockaddr = match (to_native(addr)) { - a: rt::sockaddr => a, - invalid => return errors::unsupported, // path too long + case a: rt::sockaddr => + yield a; + case invalid => + return errors::unsupported; // path too long }; const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM, 0)) { - err: rt::errno => return errors::errno(err), - fd: int => fd, + case err: rt::errno => + return errors::errno(err); + case fd: int => + yield fd; }; let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; const sz = size(rt::sockaddr_un): u32; match (rt::connect(sockfd, &sockaddr, sz)) { - err: rt::errno => return errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; static let buf: [rt::UNIX_PATH_MAX + 32]u8 = [0...]; return io::fdopen(sockfd, @@ -38,12 +43,16 @@ export fn listen( options: listen_option... ) (*net::listener | net::error) = { let sockaddr = match (to_native(addr)) { - a: rt::sockaddr => a, - invalid => return errors::unsupported, // path too long + case a: rt::sockaddr => + yield a; + case invalid => + return errors::unsupported; // path too long }; const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM, 0)) { - err: rt::errno => return errors::errno(err), - fd: int => fd, + case err: rt::errno => + return errors::errno(err); + case fd: int => + yield fd; }; let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; @@ -55,12 +64,14 @@ export fn listen( }; match (rt::bind(sockfd, &sockaddr, size(rt::sockaddr_un): u32)) { - err: rt::errno => return errors::errno(err), - int => void, + case err: rt::errno => + return errors::errno(err); + case int => void; }; - match ((rt::listen(sockfd, bk))) { - err: rt::errno => return errors::errno(err), - int => void, + match (rt::listen(sockfd, bk)) { + case err: rt::errno => + return errors::errno(err); + case int => void; }; return alloc(net::stream_listener { @@ -85,8 +96,9 @@ fn to_native(addr: addr) (rt::sockaddr | invalid) = { } }; match ((&addr: *types::string).data) { - null => void, - data: *[*]u8 => rt::memcpy(&ret.un.sun_path, data, len(addr)), + case null => void; + case data: *[*]u8 => + rt::memcpy(&ret.un.sun_path, data, len(addr)); }; ret.un.sun_path[len(addr)] = 0; return ret; diff --git a/net/unix/dial.ha b/net/unix/dial.ha @@ -3,9 +3,11 @@ use net::dial; use net; fn dial_unix(addr: str, service: str) (io::file | dial::error) = { - return match (connect(addr)) { - conn: io::file => conn, - err: net::error => err, + match (connect(addr)) { + case conn: io::file => + return conn; + case err: net::error => + return err; }; }; diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha @@ -111,14 +111,22 @@ export fn dirfdfs_set_getdents_bufsz(fs: *fs::fs, sz: size) void = { }; fn errno_to_fs(err: rt::errno) fs::error = switch (err) { - rt::ENOENT => errors::noentry, - rt::EEXIST => errors::exists, - rt::EACCES => errors::noaccess, - rt::EBUSY => errors::busy, - rt::ENOTDIR => fs::wrongtype, - rt::EOPNOTSUPP, rt::ENOSYS => errors::unsupported, - rt::EXDEV => fs::cannotrename, - * => errors::errno(err), +case rt::ENOENT => + yield errors::noentry; +case rt::EEXIST => + yield errors::exists; +case rt::EACCES => + yield errors::noaccess; +case rt::EBUSY => + yield errors::busy; +case rt::ENOTDIR => + yield fs::wrongtype; +case rt::EOPNOTSUPP, rt::ENOSYS => + yield errors::unsupported; +case rt::EXDEV => + yield fs::cannotrename; +case => + yield errors::errno(err); }; fn _fs_open( @@ -144,8 +152,10 @@ fn _fs_open( }; let fd = match (rt::openat2(fs.dirfd, path, oh, size(rt::open_how))) { - err: rt::errno => return errno_to_fs(err), - fd: int => fd, + case err: rt::errno => + return errno_to_fs(err); + case fd: int => + yield fd; }; return io::fdopen(fd, path, mode); @@ -240,16 +250,18 @@ fn fs_create( fn fs_remove(fs: *fs::fs, path: str) (void | fs::error) = { let fs = fs: *os_filesystem; match (rt::unlinkat(fs.dirfd, path, 0)) { - err: rt::errno => return errno_to_fs(err), - void => void, + case err: rt::errno => + return errno_to_fs(err); + case void => void; }; }; fn fs_rename(fs: *fs::fs, oldpath: str, newpath: str) (void | fs::error) = { let fs = fs: *os_filesystem; match (rt::renameat(fs.dirfd, oldpath, fs.dirfd, newpath, 0)) { - err: rt::errno => return errno_to_fs(err), - void => void, + case err: rt::errno => + return errno_to_fs(err); + case void => void; }; }; @@ -257,8 +269,9 @@ fn fs_stat(fs: *fs::fs, path: str) (fs::filestat | fs::error) = { let fs = fs: *os_filesystem; let st = rt::st { ... }; match (rt::fstatat(fs.dirfd, path, &st, rt::AT_SYMLINK_NOFOLLOW)) { - err: rt::errno => return errno_to_fs(err), - void => void, + case err: rt::errno => + return errno_to_fs(err); + case void => void; }; return fs::filestat { mask = fs::stat_mask::UID @@ -297,8 +310,10 @@ fn fs_subdir(fs: *fs::fs, path: str) (*fs::fs | fs::error) = { let fd: int = match (rt::openat2(fs.dirfd, path, &oh, size(rt::open_how))) { - err: rt::errno => return errno_to_fs(err), - n: int => n, + case err: rt::errno => + return errno_to_fs(err); + case fd: int => + yield fd; }; return dirfdopen(fd); @@ -307,32 +322,36 @@ fn fs_subdir(fs: *fs::fs, path: str) (*fs::fs | fs::error) = { fn fs_rmdir(fs: *fs::fs, path: str) (void | fs::error) = { let fs = fs: *os_filesystem; match (rt::unlinkat(fs.dirfd, path, rt::AT_REMOVEDIR)) { - err: rt::errno => return errno_to_fs(err), - void => void, + case err: rt::errno => + return errno_to_fs(err); + case void => void; }; }; fn fs_mkdir(fs: *fs::fs, path: str) (void | fs::error) = { let fs = fs: *os_filesystem; - return match (rt::mkdirat(fs.dirfd, path, 0o755)) { - err: rt::errno => errno_to_fs(err), - void => void, + match (rt::mkdirat(fs.dirfd, path, 0o755)) { + case err: rt::errno => + return errno_to_fs(err); + case void => void; }; }; fn fs_chmod(fs: *fs::fs, path: str, mode: fs::mode) (void | fs::error) = { let fs = fs: *os_filesystem; - return match (rt::fchmodat(fs.dirfd, path, mode: uint, 0)) { - err: rt::errno => return errno_to_fs(err), - void => void, + match (rt::fchmodat(fs.dirfd, path, mode: uint, 0)) { + case err: rt::errno => + return errno_to_fs(err); + case void => void; }; }; fn fs_chown(fs: *fs::fs, path: str, uid: uint, gid: uint) (void | fs::error) = { let fs = fs: *os_filesystem; - return match (rt::fchownat(fs.dirfd, path, uid, gid, 0)) { - err: rt::errno => return errno_to_fs(err), - void => void, + match (rt::fchownat(fs.dirfd, path, uid, gid, 0)) { + case err: rt::errno => + return errno_to_fs(err); + case void => void; }; }; @@ -353,15 +372,23 @@ fn fs_resolve(fs: *fs::fs, path: str) str = { let parts: []str = []; if (!path::abs(path)) { let iter = path::iter(getcwd()); - for (true) match (path::next(&iter)) { - void => break, - p: str => resolve_part(&parts, p), + for (true) { + match (path::next(&iter)) { + case void => + break; + case p: str => + resolve_part(&parts, p); + }; }; }; let iter = path::iter(path); - for (true) match (path::next(&iter)) { - void => break, - p: str => resolve_part(&parts, p), + for (true) { + match (path::next(&iter)) { + case void => + break; + case p: str => + resolve_part(&parts, p); + }; }; return path::join(parts...); }; @@ -390,13 +417,17 @@ fn fs_iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = { }; let fd: int = match (rt::openat2(fs.dirfd, path, &oh, size(rt::open_how))) { - err: rt::errno => return errno_to_fs(err), - n: int => n, + case err: rt::errno => + return errno_to_fs(err); + case fd: int => + yield fd; }; let buf = match (rt::malloc(fs.getdents_bufsz)) { - v: *void => v: *[*]u8, - null => return errors::nomem, + case v: *void => + yield v: *[*]u8; + case null => + return errors::nomem; }; let iter = alloc(os_iterator { iter = fs::iterator { @@ -428,15 +459,24 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { let name = strings::fromc(&de.d_name: *const char); let ftype: fs::mode = switch (de.d_type) { - rt::DT_UNKNOWN => fs::mode::UNKNOWN, - rt::DT_FIFO => fs::mode::FIFO, - rt::DT_CHR => fs::mode::CHR, - rt::DT_DIR => fs::mode::DIR, - rt::DT_BLK => fs::mode::BLK, - rt::DT_REG => fs::mode::REG, - rt::DT_LNK => fs::mode::LINK, - rt::DT_SOCK => fs::mode::SOCK, - * => fs::mode::UNKNOWN, + case rt::DT_UNKNOWN => + yield fs::mode::UNKNOWN; + case rt::DT_FIFO => + yield fs::mode::FIFO; + case rt::DT_CHR => + yield fs::mode::CHR; + case rt::DT_DIR => + yield fs::mode::DIR; + case rt::DT_BLK => + yield fs::mode::BLK; + case rt::DT_REG => + yield fs::mode::REG; + case rt::DT_LNK => + yield fs::mode::LINK; + case rt::DT_SOCK => + yield fs::mode::SOCK; + case => + yield fs::mode::UNKNOWN; }; return fs::dirent { name = name, diff --git a/os/+linux/environ.ha b/os/+linux/environ.ha @@ -38,9 +38,11 @@ export fn getenv(name: const str) (str | void) = { for (let i = 0z; rt::envp[i] != null; i += 1) { const item = rt::envp[i]: *[*]u8; const ln = strings::cstrlen(item: *char); - const eq: size = match (bytes::index(item[..ln], '=': u32: u8)) { - void => abort("Environment violates System-V invariants"), - i: size => i, + const eq: size = match (bytes::index(item[..ln], '=': u8)) { + case void => + abort("Environment violates System-V invariants"); + case i: size => + yield i; }; if (bytes::equal(name_b, item[..eq])) { const ln = strings::cstrlen(item: *const char); @@ -52,8 +54,10 @@ export fn getenv(name: const str) (str | void) = { // Looks up an environment variable and returns its value, or a default value if // unset. export fn tryenv(name: const str, default: str) str = match (getenv(name)) { - s: str => s, - void => default, +case s: str => + yield s; +case void => + yield default; }; let envp: []str = []; diff --git a/os/+linux/fs.ha b/os/+linux/fs.ha @@ -17,19 +17,22 @@ export fn getcwd() str = strings::fromc(rt::getcwd() as *const char); // Change the current working directory. export fn chdir(target: (*fs::fs | str)) (void | fs::error) = { const path: str = match (target) { - fs: *fs::fs => { - assert(fs.open == &fs_open); - let fs = fs: *os_filesystem; - return match (rt::fchdir(fs.dirfd)) { - err: rt::errno => errors::errno(err), - void => void, - }; - }, - s: str => s, + case fs: *fs::fs => + assert(fs.open == &fs_open); + let fs = fs: *os_filesystem; + match (rt::fchdir(fs.dirfd)) { + case err: rt::errno => + return errors::errno(err); + case void => + return; + }; + case s: str => + yield s; }; - return match (rt::chdir(path)) { - err: rt::errno => errors::errno(err), - void => void, + match (rt::chdir(path)) { + case err: rt::errno => + return errors::errno(err); + case void => void; }; }; @@ -38,18 +41,20 @@ export fn chdir(target: (*fs::fs | str)) (void | fs::error) = { // // This function is not appropriate for sandboxing. export fn chroot(target: str) (void | fs::error) = { - return match (rt::chroot(target)) { - err: rt::errno => errors::errno(err), - void => void, + match (rt::chroot(target)) { + case err: rt::errno => + return errors::errno(err); + case void => void; }; }; // Makes a FIFO node. This function is only available on Unix systems. export fn mkfifo(path: str, mode: fs::mode) (void | fs::error) = { - return match (rt::mknodat(rt::AT_FDCWD, path, + match (rt::mknodat(rt::AT_FDCWD, path, mode: rt::mode_t | rt::S_IFIFO, 0)) { - err: rt::errno => errors::errno(err), - void => void, + case err: rt::errno => + return errors::errno(err); + case void => void; }; }; @@ -60,11 +65,12 @@ export fn mkblk( major: uint, minor: uint, ) (void | fs::error) = { - return match (rt::mknodat(rt::AT_FDCWD, path, + match (rt::mknodat(rt::AT_FDCWD, path, mode: rt::mode_t | rt::S_IFBLK, rt::mkdev(major: u32, minor: u32))) { - err: rt::errno => errors::errno(err), - void => void, + case err: rt::errno => + return errors::errno(err); + case void => void; }; }; @@ -75,10 +81,10 @@ export fn mkchr( major: uint, minor: uint, ) (void | fs::error) = { - return match (rt::mknodat(rt::AT_FDCWD, path, - mode: rt::mode_t | rt::S_IFCHR, + match (rt::mknodat(rt::AT_FDCWD, path, mode: rt::mode_t | rt::S_IFCHR, rt::mkdev(major: u32, minor: u32))) { - err: rt::errno => errors::errno(err), - void => void, + case err: rt::errno => + return errors::errno(err); + case void => void; }; }; diff --git a/os/exec/cmd.ha b/os/exec/cmd.ha @@ -14,18 +14,26 @@ use strings; // let proc = exec::start(&cmd); // let status = exec::wait(&proc); // assert(exec::status(status) == 0); -// +// // By default, the new command will inherit the current process's environment. export fn cmd(name: str, args: str...) (command | error) = { let env = os::getenvs(); let cmd = command { platform: platform_cmd = - if (strings::contains(name, '/')) match (open(name)) { - p: platform_cmd => p, - * => return nocmd, - } else match (lookup(name)) { - void => return nocmd, - p: platform_cmd => p, + if (strings::contains(name, '/')) { + yield match (open(name)) { + case p: platform_cmd => + yield p; + case => + return nocmd; + }; + } else { + yield match (lookup(name)) { + case void => + return nocmd; + case p: platform_cmd => + yield p; + }; }, argv = alloc([], len(args) + 1z), env = alloc([], len(env)), @@ -61,9 +69,11 @@ export @noreturn fn exec(cmd: *command) void = { // Starts a prepared command in a new process. export fn start(cmd: *command) (error | process) = { defer finish(cmd); - return match (platform_start(cmd)) { - err: errors::opaque => err, - proc: process => proc, + match (platform_start(cmd)) { + case err: errors::opaque => + return err; + case proc: process => + return proc; }; }; @@ -80,11 +90,16 @@ export fn clearenv(cmd: *command) void = { export fn setenv(cmd: *command, key: str, value: str) void = { let iter = strings::iter(key); for (let i = 0z; true; i += 1) match (strings::next(&iter)) { - void => break, - r: rune => if (i == 0) assert(r == '_' || ascii::isalpha(r), - "Invalid environment variable") - else assert(r == '_' || ascii::isalnum(r), - "Invalid environment variable"), + case void => + break; + case r: rune => + if (i == 0) { + assert(r == '_' || ascii::isalpha(r), + "Invalid environment variable"); + } else { + assert(r == '_' || ascii::isalnum(r), + "Invalid environment variable"); + }; }; // XXX: This can be a binary search @@ -101,20 +116,26 @@ export fn setenv(cmd: *command, key: str, value: str) void = { fn lookup(name: str) (platform_cmd | void) = { const path = match (os::getenv("PATH")) { - void => return, - s: str => s, + case void => + return; + case s: str => + yield s; }; let tok = strings::tokenize(path, ":"); for (true) { const item = match (strings::next_token(&tok)) { - void => break, - s: str => s, + case void => + break; + case s: str => + yield s; }; let path = strings::concat(item, "/", name); defer free(path); match (open(path)) { - err: error => continue, - p: platform_cmd => return p, + case err: error => + continue; + case p: platform_cmd => + return p; }; }; }; diff --git a/os/exec/exec+linux.ha b/os/exec/exec+linux.ha @@ -7,21 +7,31 @@ export type platform_cmd = int; // Forks the current process, returning the pid of the child (to the parent) and // void (to the child), or an error. -export fn fork() (int | void | error) = match (rt::fork()) { - err: rt::errno => errors::errno(err), - i: (int | void) => i, +export fn fork() (int | void | error) = { + match (rt::fork()) { + case err: rt::errno => + return errors::errno(err); + case i: (int | void) => + return i; + }; }; fn open(path: str) (platform_cmd | error) = { match (rt::access(path, rt::X_OK)) { - err: rt::errno => return errors::errno(err), - b: bool => if (!b) return errors::errno(rt::EACCES), + case err: rt::errno => + return errors::errno(err); + case b: bool => + if (!b) { + return errors::noaccess; + }; }; // O_PATH is used because it allows us to use an executable for which we // have execute permissions, but not read permissions. - return match (rt::open(path, rt::O_PATH, 0u)) { - fd: int => fd, - err: rt::errno => errors::errno(err), + match (rt::open(path, rt::O_PATH, 0u)) { + case fd: int => + return fd; + case err: rt::errno => + return errors::errno(err); }; }; @@ -54,34 +64,39 @@ fn platform_start(cmd: *command) (process | errors::error) = { // TODO: Let the user configure clone more to their taste (e.g. SIGCHLD) let pipe: [2]int = [0...]; match (rt::pipe2(&pipe, rt::O_CLOEXEC)) { - err: rt::errno => return errors::errno(err), - void => void, + case err: rt::errno => + return errors::errno(err); + case void => void; }; match (rt::clone(null, 0, null, null, 0)) { - err: rt::errno => return errors::errno(err), - pid: int => { - rt::close(pipe[1])!; - let errno: int = 0; - return match (rt::read(pipe[0], &errno, size(int))) { - err: rt::errno => errors::errno(err), - n: size => switch (n) { - size(int) => errors::errno(errno), - * => abort("Unexpected rt::read result"), - 0 => pid, - }, - }; - }, - void => { - rt::close(pipe[0])!; - let err = platform_exec(cmd); - if (!(err is errors::opaque)) { - rt::exit(1); + case err: rt::errno => + return errors::errno(err); + case pid: int => + rt::close(pipe[1])!; + let errno: int = 0; + match (rt::read(pipe[0], &errno, size(int))) { + case err: rt::errno => + return errors::errno(err); + case n: size => + switch (n) { + case size(int) => + return errors::errno(errno); + case 0 => + return pid; + case => + abort("Unexpected rt::read result"); }; - let err = err as errors::opaque; - let err = &err.data: *rt::errno; - rt::write(pipe[1], &err, size(int))!; + }; + case void => + rt::close(pipe[0])!; + let err = platform_exec(cmd); + if (!(err is errors::opaque)) { rt::exit(1); - }, + }; + let err = err as errors::opaque; + let err = &err.data: *rt::errno; + rt::write(pipe[1], &err, size(int))!; + rt::exit(1); }; }; diff --git a/os/exec/process+linux.ha b/os/exec/process+linux.ha @@ -38,8 +38,10 @@ export fn wait(proc: *process) (status | error) = { let ru: rt::rusage = rt::rusage { ... }; let st: status = status { ... }; match (rt::wait4(*proc, &st.status, 0, &ru)) { - err: rt::errno => return errors::errno(err), - pid: int => assert(pid == *proc), + case err: rt::errno => + return errors::errno(err); + case pid: int => + assert(pid == *proc); }; rusage(&st, &ru); return st; @@ -51,11 +53,15 @@ export fn peek(proc: *process) (status | void | error) = { let ru: rt::rusage = rt::rusage { ... }; let st: status = status { ... }; match (rt::wait4(*proc, &st.status, 0, &ru)) { - err: rt::errno => return errors::errno(err), - pid: int => switch (pid) { - 0 => return void, - * => assert(pid == *proc), - }, + case err: rt::errno => + return errors::errno(err); + case pid: int => + switch (pid) { + case 0 => + return; + case => + assert(pid == *proc); + }; }; rusage(&st, &ru); return st; @@ -73,13 +79,18 @@ export type exit_status = (exited | signaled); // Returns a human friendly string describing the exit status. export fn exitstr(status: exit_status) const str = { static let buf: [1024]u8 = [0...]; - return match (status) { - i: exited => switch (i) { - 0 => "exited normally", - * => fmt::bsprintf(buf, "exited with status {}", i: int), - }, + match (status) { + case i: exited => + switch (i) { + case 0 => + return "exited normally"; + case => + return fmt::bsprintf(buf, "exited with status {}", + i: int); + }; + case s: signaled => // TODO: Add signal name - s: signaled => fmt::bsprintf(buf, "exited with signal {}", s: int), + return fmt::bsprintf(buf, "exited with signal {}", s: int); }; }; @@ -98,9 +109,11 @@ export fn exit(stat: *status) exit_status = { // or its status code as an error type if not. export fn check(stat: *status) (void | !exit_status) = { if (rt::wifexited(stat.status)) { - return switch (rt::wexitstatus(stat.status)) { - 0 => void, - * => exit(stat), + switch (rt::wexitstatus(stat.status)) { + case 0 => + return void; + case => + return exit(stat); }; }; return exit(stat); diff --git a/os/exec/types.ha b/os/exec/types.ha @@ -15,9 +15,11 @@ export type error = !(nocmd | ...errors::error); // Returns a human-readable message for the given error. export fn strerror(err: error) const str = { - return match (err) { - nocmd => "Command not found", - err: errors::opaque => errors::strerror(err), + match (err) { + case nocmd => + return "Command not found"; + case err: errors::opaque => + return errors::strerror(err); }; }; diff --git a/path/iter.ha b/path/iter.ha @@ -39,9 +39,10 @@ export fn next(iter: *iterator) (str | void) = { static assert(PATHSEP <= 0x7F); return strings::fromutf8_unsafe(pathsep); }; - return match (bytes::next_token(&iter.tok)) { - b: []u8 => strings::fromutf8_unsafe(b), - void => void, + match (bytes::next_token(&iter.tok)) { + case b: []u8 => + return strings::fromutf8_unsafe(b); + case void => void; }; }; diff --git a/path/names.ha b/path/names.ha @@ -9,8 +9,10 @@ use strings; export fn dirname(path: str) str = { let b = strings::toutf8(path); let i = match (bytes::rindex(b, PATHSEP)) { - void => return path, - z: size => z, + case void => + return path; + case z: size => + yield z; }; if (i == 0) { i += 1; @@ -33,8 +35,10 @@ export fn dirname(path: str) str = { export fn basename(path: str) str = { let b = strings::toutf8(path); let i = match (bytes::rindex(b, PATHSEP)) { - void => return path, - z: size => if (z + 1 < len(b)) z + 1z else 0z, + case void => + return path; + case z: size => + yield if (z + 1 < len(b)) z + 1z else 0z; }; return strings::fromutf8_unsafe(b[i..]); }; @@ -62,8 +66,10 @@ export fn extension(p: str) (str, str) = { return (p, ""); }; let i = match (bytes::index(b, '.': u32: u8)) { - void => return (p, ""), - z: size => z, + case void => + return (p, ""); + case z: size => + yield z; }; let e = b[i..]; let n = b[..i]; diff --git a/rt/+linux/+x86_64.ha b/rt/+linux/+x86_64.ha @@ -7,17 +7,19 @@ export fn clone( child_tid: nullable *int, tls: u64, ) (int | void | errno) = { - return match (wrap_return(syscall5(SYS_clone, - flags: u64, - stack: uintptr: u64, - parent_tid: uintptr: u64, - child_tid: uintptr: u64, - tls))) { - u: u64 => switch (u) { - 0 => void, - * => u: int, - }, - err: errno => err, + match (wrap_return(syscall5(SYS_clone, + flags: u64, stack: uintptr: u64, + parent_tid: uintptr: u64, child_tid: uintptr: u64, + tls))) { + case u: u64 => + switch (u) { + case 0 => + return; + case => + return u: int; + }; + case err: errno => + return err; }; }; diff --git a/rt/+linux/errno.ha b/rt/+linux/errno.ha @@ -17,276 +17,540 @@ fn wrap_return(r: u64) (errno | u64) = { // permitted"). export fn strerror(err: errno) str = { return switch (err: int) { - EPERM => "Operation not permitted", - ENOENT => "No such file or directory", - ESRCH => "No such process", - EINTR => "Interrupted system call", - EIO => "Input/output error", - ENXIO => "No such device or address", - E2BIG => "Argument list too long", - ENOEXEC => "Exec format error", - EBADF => "Bad file descriptor", - ECHILD => "No child processes", - EAGAIN => "Resource temporarily unavailable", - ENOMEM => "Cannot allocate memory", - EACCES => "Permission denied", - EFAULT => "Bad address", - ENOTBLK => "Block device required", - EBUSY => "Device or resource busy", - EEXIST => "File exists", - EXDEV => "Invalid cross-device link", - ENODEV => "No such device", - ENOTDIR => "Not a directory", - EISDIR => "Is a directory", - EINVAL => "Invalid argument", - ENFILE => "Too many open files in system", - EMFILE => "Too many open files", - ENOTTY => "Inappropriate ioctl for device", - ETXTBSY => "Text file busy", - EFBIG => "File too large", - ENOSPC => "No space left on device", - ESPIPE => "Illegal seek", - EROFS => "Read-only file system", - EMLINK => "Too many links", - EPIPE => "Broken pipe", - EDOM => "Numerical argument out of domain", - ERANGE => "Numerical result out of range", - EDEADLK => "Resource deadlock avoided", - ENAMETOOLONG => "File name too long", - ENOLCK => "No locks available", - ENOSYS => "Function not implemented", - ENOTEMPTY => "Directory not empty", - ELOOP => "Too many levels of symbolic links", - ENOMSG => "No message of desired type", - EIDRM => "Identifier removed", - ECHRNG => "Channel number out of range", - EL2NSYNC => "Level 2 not synchronized", - EL3HLT => "Level 3 halted", - EL3RST => "Level 3 reset", - ELNRNG => "Link number out of range", - EUNATCH => "Protocol driver not attached", - ENOCSI => "No CSI structure available", - EL2HLT => "Level 2 halted", - EBADE => "Invalid exchange", - EBADR => "Invalid request descriptor", - EXFULL => "Exchange full", - ENOANO => "No anode", - EBADRQC => "Invalid request code", - EBADSLT => "Invalid slot", - EBFONT => "Bad font file format", - ENOSTR => "Device not a stream", - ENODATA => "No data available", - ETIME => "Timer expired", - ENOSR => "Out of streams resources", - ENONET => "Machine is not on the network", - ENOPKG => "Package not installed", - EREMOTE => "Object is remote", - ENOLINK => "Link has been severed", - EADV => "Advertise error", - ESRMNT => "Srmount error", - ECOMM => "Communication error on send", - EPROTO => "Protocol error", - EMULTIHOP => "Multihop attempted", - EDOTDOT => "RFS specific error", - EBADMSG => "Bad message", - EOVERFLOW => "Value too large for defined data type", - ENOTUNIQ => "Name not unique on network", - EBADFD => "File descriptor in bad state", - EREMCHG => "Remote address changed", - ELIBACC => "Can not access a needed shared library", - ELIBBAD => "Accessing a corrupted shared library", - ELIBSCN => ".lib section in a.out corrupted", - ELIBMAX => "Attempting to link in too many shared libraries", - ELIBEXEC => "Cannot exec a shared library directly", - EILSEQ => "Invalid or incomplete multibyte or wide character", - ERESTART => "Interrupted system call should be restarted", - ESTRPIPE => "Streams pipe error", - EUSERS => "Too many users", - ENOTSOCK => "Socket operation on non-socket", - EDESTADDRREQ => "Destination address required", - EMSGSIZE => "Message too long", - EPROTOTYPE => "Protocol wrong type for socket", - ENOPROTOOPT => "Protocol not available", - EPROTONOSUPPORT => "Protocol not supported", - ESOCKTNOSUPPORT => "Socket type not supported", - EOPNOTSUPP => "Operation not supported", - EPFNOSUPPORT => "Protocol family not supported", - EAFNOSUPPORT => "Address family not supported by protocol", - EADDRINUSE => "Address already in use", - EADDRNOTAVAIL => "Cannot assign requested address", - ENETDOWN => "Network is down", - ENETUNREACH => "Network is unreachable", - ENETRESET => "Network dropped connection on reset", - ECONNABORTED => "Software caused connection abort", - ECONNRESET => "Connection reset by peer", - ENOBUFS => "No buffer space available", - EISCONN => "Transport endpoint is already connected", - ENOTCONN => "Transport endpoint is not connected", - ESHUTDOWN => "Cannot send after transport endpoint shutdown", - ETOOMANYREFS => "Too many references: cannot splice", - ETIMEDOUT => "Connection timed out", - ECONNREFUSED => "Connection refused", - EHOSTDOWN => "Host is down", - EHOSTUNREACH => "No route to host", - EALREADY => "Operation already in progress", - EINPROGRESS => "Operation now in progress", - ESTALE => "Stale file handle", - EUCLEAN => "Structure needs cleaning", - ENOTNAM => "Not a XENIX named type file", - ENAVAIL => "No XENIX semaphores available", - EISNAM => "Is a named type file", - EREMOTEIO => "Remote I/O error", - EDQUOT => "Disk quota exceeded", - ENOMEDIUM => "No medium found", - EMEDIUMTYPE => "Wrong medium type", - ECANCELED => "Operation canceled", - ENOKEY => "Required key not available", - EKEYEXPIRED => "Key has expired", - EKEYREVOKED => "Key has been revoked", - EKEYREJECTED => "Key was rejected by service", - EOWNERDEAD => "Owner died", - ENOTRECOVERABLE => "State not recoverable", - ERFKILL => "Operation not possible due to RF-kill", - EHWPOISON => "Memory page has hardware error", - * => "Unknown Linux error code", // TODO: snprintf to add errno? + case EPERM => + yield "Operation not permitted"; + case ENOENT => + yield "No such file or directory"; + case ESRCH => + yield "No such process"; + case EINTR => + yield "Interrupted system call"; + case EIO => + yield "Input/output error"; + case ENXIO => + yield "No such device or address"; + case E2BIG => + yield "Argument list too long"; + case ENOEXEC => + yield "Exec format error"; + case EBADF => + yield "Bad file descriptor"; + case ECHILD => + yield "No child processes"; + case EAGAIN => + yield "Resource temporarily unavailable"; + case ENOMEM => + yield "Cannot allocate memory"; + case EACCES => + yield "Permission denied"; + case EFAULT => + yield "Bad address"; + case ENOTBLK => + yield "Block device required"; + case EBUSY => + yield "Device or resource busy"; + case EEXIST => + yield "File exists"; + case EXDEV => + yield "Invalid cross-device link"; + case ENODEV => + yield "No such device"; + case ENOTDIR => + yield "Not a directory"; + case EISDIR => + yield "Is a directory"; + case EINVAL => + yield "Invalid argument"; + case ENFILE => + yield "Too many open files in system"; + case EMFILE => + yield "Too many open files"; + case ENOTTY => + yield "Inappropriate ioctl for device"; + case ETXTBSY => + yield "Text file busy"; + case EFBIG => + yield "File too large"; + case ENOSPC => + yield "No space left on device"; + case ESPIPE => + yield "Illegal seek"; + case EROFS => + yield "Read-only file system"; + case EMLINK => + yield "Too many links"; + case EPIPE => + yield "Broken pipe"; + case EDOM => + yield "Numerical argument out of domain"; + case ERANGE => + yield "Numerical result out of range"; + case EDEADLK => + yield "Resource deadlock avoided"; + case ENAMETOOLONG => + yield "File name too long"; + case ENOLCK => + yield "No locks available"; + case ENOSYS => + yield "Function not implemented"; + case ENOTEMPTY => + yield "Directory not empty"; + case ELOOP => + yield "Too many levels of symbolic links"; + case ENOMSG => + yield "No message of desired type"; + case EIDRM => + yield "Identifier removed"; + case ECHRNG => + yield "Channel number out of range"; + case EL2NSYNC => + yield "Level 2 not synchronized"; + case EL3HLT => + yield "Level 3 halted"; + case EL3RST => + yield "Level 3 reset"; + case ELNRNG => + yield "Link number out of range"; + case EUNATCH => + yield "Protocol driver not attached"; + case ENOCSI => + yield "No CSI structure available"; + case EL2HLT => + yield "Level 2 halted"; + case EBADE => + yield "Invalid exchange"; + case EBADR => + yield "Invalid request descriptor"; + case EXFULL => + yield "Exchange full"; + case ENOANO => + yield "No anode"; + case EBADRQC => + yield "Invalid request code"; + case EBADSLT => + yield "Invalid slot"; + case EBFONT => + yield "Bad font file format"; + case ENOSTR => + yield "Device not a stream"; + case ENODATA => + yield "No data available"; + case ETIME => + yield "Timer expired"; + case ENOSR => + yield "Out of streams resources"; + case ENONET => + yield "Machine is not on the network"; + case ENOPKG => + yield "Package not installed"; + case EREMOTE => + yield "Object is remote"; + case ENOLINK => + yield "Link has been severed"; + case EADV => + yield "Advertise error"; + case ESRMNT => + yield "Srmount error"; + case ECOMM => + yield "Communication error on send"; + case EPROTO => + yield "Protocol error"; + case EMULTIHOP => + yield "Multihop attempted"; + case EDOTDOT => + yield "RFS specific error"; + case EBADMSG => + yield "Bad message"; + case EOVERFLOW => + yield "Value too large for defined data type"; + case ENOTUNIQ => + yield "Name not unique on network"; + case EBADFD => + yield "File descriptor in bad state"; + case EREMCHG => + yield "Remote address changed"; + case ELIBACC => + yield "Can not access a needed shared library"; + case ELIBBAD => + yield "Accessing a corrupted shared library"; + case ELIBSCN => + yield ".lib section in a.out corrupted"; + case ELIBMAX => + yield "Attempting to link in too many shared libraries"; + case ELIBEXEC => + yield "Cannot exec a shared library directly"; + case EILSEQ => + yield "Invalid or incomplete multibyte or wide character"; + case ERESTART => + yield "Interrupted system call should be restarted"; + case ESTRPIPE => + yield "Streams pipe error"; + case EUSERS => + yield "Too many users"; + case ENOTSOCK => + yield "Socket operation on non-socket"; + case EDESTADDRREQ => + yield "Destination address required"; + case EMSGSIZE => + yield "Message too long"; + case EPROTOTYPE => + yield "Protocol wrong type for socket"; + case ENOPROTOOPT => + yield "Protocol not available"; + case EPROTONOSUPPORT => + yield "Protocol not supported"; + case ESOCKTNOSUPPORT => + yield "Socket type not supported"; + case EOPNOTSUPP => + yield "Operation not supported"; + case EPFNOSUPPORT => + yield "Protocol family not supported"; + case EAFNOSUPPORT => + yield "Address family not supported by protocol"; + case EADDRINUSE => + yield "Address already in use"; + case EADDRNOTAVAIL => + yield "Cannot assign requested address"; + case ENETDOWN => + yield "Network is down"; + case ENETUNREACH => + yield "Network is unreachable"; + case ENETRESET => + yield "Network dropped connection on reset"; + case ECONNABORTED => + yield "Software caused connection abort"; + case ECONNRESET => + yield "Connection reset by peer"; + case ENOBUFS => + yield "No buffer space available"; + case EISCONN => + yield "Transport endpoint is already connected"; + case ENOTCONN => + yield "Transport endpoint is not connected"; + case ESHUTDOWN => + yield "Cannot send after transport endpoint shutdown"; + case ETOOMANYREFS => + yield "Too many references: cannot splice"; + case ETIMEDOUT => + yield "Connection timed out"; + case ECONNREFUSED => + yield "Connection refused"; + case EHOSTDOWN => + yield "Host is down"; + case EHOSTUNREACH => + yield "No route to host"; + case EALREADY => + yield "Operation already in progress"; + case EINPROGRESS => + yield "Operation now in progress"; + case ESTALE => + yield "Stale file handle"; + case EUCLEAN => + yield "Structure needs cleaning"; + case ENOTNAM => + yield "Not a XENIX named type file"; + case ENAVAIL => + yield "No XENIX semaphores available"; + case EISNAM => + yield "Is a named type file"; + case EREMOTEIO => + yield "Remote I/O error"; + case EDQUOT => + yield "Disk quota exceeded"; + case ENOMEDIUM => + yield "No medium found"; + case EMEDIUMTYPE => + yield "Wrong medium type"; + case ECANCELED => + yield "Operation canceled"; + case ENOKEY => + yield "Required key not available"; + case EKEYEXPIRED => + yield "Key has expired"; + case EKEYREVOKED => + yield "Key has been revoked"; + case EKEYREJECTED => + yield "Key was rejected by service"; + case EOWNERDEAD => + yield "Owner died"; + case ENOTRECOVERABLE => + yield "State not recoverable"; + case ERFKILL => + yield "Operation not possible due to RF-kill"; + case EHWPOISON => + yield "Memory page has hardware error"; + case => + yield "Unknown Linux error code"; // TODO: snprintf to add errno? }; }; // Gets the programmer-friendly name for an [[errno]] (e.g. EPERM). export fn errname(err: errno) str = { return switch (err: int) { - EPERM => "EPERM", - ENOENT => "ENOENT", - ESRCH => "ESRCH", - EINTR => "EINTR", - EIO => "EIO", - ENXIO => "ENXIO", - E2BIG => "E2BIG", - ENOEXEC => "ENOEXEC", - EBADF => "EBADF", - ECHILD => "ECHILD", - EAGAIN => "EAGAIN", - ENOMEM => "ENOMEM", - EACCES => "EACCES", - EFAULT => "EFAULT", - ENOTBLK => "ENOTBLK", - EBUSY => "EBUSY", - EEXIST => "EEXIST", - EXDEV => "EXDEV", - ENODEV => "ENODEV", - ENOTDIR => "ENOTDIR", - EISDIR => "EISDIR", - EINVAL => "EINVAL", - ENFILE => "ENFILE", - EMFILE => "EMFILE", - ENOTTY => "ENOTTY", - ETXTBSY => "ETXTBSY", - EFBIG => "EFBIG", - ENOSPC => "ENOSPC", - ESPIPE => "ESPIPE", - EROFS => "EROFS", - EMLINK => "EMLINK", - EPIPE => "EPIPE", - EDOM => "EDOM", - ERANGE => "ERANGE", - EDEADLK => "EDEADLK", - ENAMETOOLONG => "ENAMETOOLONG", - ENOLCK => "ENOLCK", - ENOSYS => "ENOSYS", - ENOTEMPTY => "ENOTEMPTY", - ELOOP => "ELOOP", - ENOMSG => "ENOMSG", - EIDRM => "EIDRM", - ECHRNG => "ECHRNG", - EL2NSYNC => "EL2NSYNC", - EL3HLT => "EL3HLT", - EL3RST => "EL3RST", - ELNRNG => "ELNRNG", - EUNATCH => "EUNATCH", - ENOCSI => "ENOCSI", - EL2HLT => "EL2HLT", - EBADE => "EBADE", - EBADR => "EBADR", - EXFULL => "EXFULL", - ENOANO => "ENOANO", - EBADRQC => "EBADRQC", - EBADSLT => "EBADSLT", - EBFONT => "EBFONT", - ENOSTR => "ENOSTR", - ENODATA => "ENODATA", - ETIME => "ETIME", - ENOSR => "ENOSR", - ENONET => "ENONET", - ENOPKG => "ENOPKG", - EREMOTE => "EREMOTE", - ENOLINK => "ENOLINK", - EADV => "EADV", - ESRMNT => "ESRMNT", - ECOMM => "ECOMM", - EPROTO => "EPROTO", - EMULTIHOP => "EMULTIHOP", - EDOTDOT => "EDOTDOT", - EBADMSG => "EBADMSG", - EOVERFLOW => "EOVERFLOW", - ENOTUNIQ => "ENOTUNIQ", - EBADFD => "EBADFD", - EREMCHG => "EREMCHG", - ELIBACC => "ELIBACC", - ELIBBAD => "ELIBBAD", - ELIBSCN => "ELIBSCN", - ELIBMAX => "ELIBMAX", - ELIBEXEC => "ELIBEXEC", - EILSEQ => "EILSEQ", - ERESTART => "ERESTART", - ESTRPIPE => "ESTRPIPE", - EUSERS => "EUSERS", - ENOTSOCK => "ENOTSOCK", - EDESTADDRREQ => "EDESTADDRREQ", - EMSGSIZE => "EMSGSIZE", - EPROTOTYPE => "EPROTOTYPE", - ENOPROTOOPT => "ENOPROTOOPT", - EPROTONOSUPPORT => "EPROTONOSUPPORT", - ESOCKTNOSUPPORT => "ESOCKTNOSUPPORT", - EOPNOTSUPP => "EOPNOTSUPP", - EPFNOSUPPORT => "EPFNOSUPPORT", - EAFNOSUPPORT => "EAFNOSUPPORT", - EADDRINUSE => "EADDRINUSE", - EADDRNOTAVAIL => "EADDRNOTAVAIL", - ENETDOWN => "ENETDOWN", - ENETUNREACH => "ENETUNREACH", - ENETRESET => "ENETRESET", - ECONNABORTED => "ECONNABORTED", - ECONNRESET => "ECONNRESET", - ENOBUFS => "ENOBUFS", - EISCONN => "EISCONN", - ENOTCONN => "ENOTCONN", - ESHUTDOWN => "ESHUTDOWN", - ETOOMANYREFS => "ETOOMANYREFS", - ETIMEDOUT => "ETIMEDOUT", - ECONNREFUSED => "ECONNREFUSED", - EHOSTDOWN => "EHOSTDOWN", - EHOSTUNREACH => "EHOSTUNREACH", - EALREADY => "EALREADY", - EINPROGRESS => "EINPROGRESS", - ESTALE => "ESTALE", - EUCLEAN => "EUCLEAN", - ENOTNAM => "ENOTNAM", - ENAVAIL => "ENAVAIL", - EISNAM => "EISNAM", - EREMOTEIO => "EREMOTEIO", - EDQUOT => "EDQUOT", - ENOMEDIUM => "ENOMEDIUM", - EMEDIUMTYPE => "EMEDIUMTYPE", - ECANCELED => "ECANCELED", - ENOKEY => "ENOKEY", - EKEYEXPIRED => "EKEYEXPIRED", - EKEYREVOKED => "EKEYREVOKED", - EKEYREJECTED => "EKEYREJECTED", - EOWNERDEAD => "EOWNERDEAD", - ENOTRECOVERABLE => "ENOTRECOVERABLE", - ERFKILL => "ERFKILL", - EHWPOISON => "EHWPOISON", - * => "[unknown errno]", // TODO: snprintf to add errno? + case EPERM => + yield "EPERM"; + case ENOENT => + yield "ENOENT"; + case ESRCH => + yield "ESRCH"; + case EINTR => + yield "EINTR"; + case EIO => + yield "EIO"; + case ENXIO => + yield "ENXIO"; + case E2BIG => + yield "E2BIG"; + case ENOEXEC => + yield "ENOEXEC"; + case EBADF => + yield "EBADF"; + case ECHILD => + yield "ECHILD"; + case EAGAIN => + yield "EAGAIN"; + case ENOMEM => + yield "ENOMEM"; + case EACCES => + yield "EACCES"; + case EFAULT => + yield "EFAULT"; + case ENOTBLK => + yield "ENOTBLK"; + case EBUSY => + yield "EBUSY"; + case EEXIST => + yield "EEXIST"; + case EXDEV => + yield "EXDEV"; + case ENODEV => + yield "ENODEV"; + case ENOTDIR => + yield "ENOTDIR"; + case EISDIR => + yield "EISDIR"; + case EINVAL => + yield "EINVAL"; + case ENFILE => + yield "ENFILE"; + case EMFILE => + yield "EMFILE"; + case ENOTTY => + yield "ENOTTY"; + case ETXTBSY => + yield "ETXTBSY"; + case EFBIG => + yield "EFBIG"; + case ENOSPC => + yield "ENOSPC"; + case ESPIPE => + yield "ESPIPE"; + case EROFS => + yield "EROFS"; + case EMLINK => + yield "EMLINK"; + case EPIPE => + yield "EPIPE"; + case EDOM => + yield "EDOM"; + case ERANGE => + yield "ERANGE"; + case EDEADLK => + yield "EDEADLK"; + case ENAMETOOLONG => + yield "ENAMETOOLONG"; + case ENOLCK => + yield "ENOLCK"; + case ENOSYS => + yield "ENOSYS"; + case ENOTEMPTY => + yield "ENOTEMPTY"; + case ELOOP => + yield "ELOOP"; + case ENOMSG => + yield "ENOMSG"; + case EIDRM => + yield "EIDRM"; + case ECHRNG => + yield "ECHRNG"; + case EL2NSYNC => + yield "EL2NSYNC"; + case EL3HLT => + yield "EL3HLT"; + case EL3RST => + yield "EL3RST"; + case ELNRNG => + yield "ELNRNG"; + case EUNATCH => + yield "EUNATCH"; + case ENOCSI => + yield "ENOCSI"; + case EL2HLT => + yield "EL2HLT"; + case EBADE => + yield "EBADE"; + case EBADR => + yield "EBADR"; + case EXFULL => + yield "EXFULL"; + case ENOANO => + yield "ENOANO"; + case EBADRQC => + yield "EBADRQC"; + case EBADSLT => + yield "EBADSLT"; + case EBFONT => + yield "EBFONT"; + case ENOSTR => + yield "ENOSTR"; + case ENODATA => + yield "ENODATA"; + case ETIME => + yield "ETIME"; + case ENOSR => + yield "ENOSR"; + case ENONET => + yield "ENONET"; + case ENOPKG => + yield "ENOPKG"; + case EREMOTE => + yield "EREMOTE"; + case ENOLINK => + yield "ENOLINK"; + case EADV => + yield "EADV"; + case ESRMNT => + yield "ESRMNT"; + case ECOMM => + yield "ECOMM"; + case EPROTO => + yield "EPROTO"; + case EMULTIHOP => + yield "EMULTIHOP"; + case EDOTDOT => + yield "EDOTDOT"; + case EBADMSG => + yield "EBADMSG"; + case EOVERFLOW => + yield "EOVERFLOW"; + case ENOTUNIQ => + yield "ENOTUNIQ"; + case EBADFD => + yield "EBADFD"; + case EREMCHG => + yield "EREMCHG"; + case ELIBACC => + yield "ELIBACC"; + case ELIBBAD => + yield "ELIBBAD"; + case ELIBSCN => + yield "ELIBSCN"; + case ELIBMAX => + yield "ELIBMAX"; + case ELIBEXEC => + yield "ELIBEXEC"; + case EILSEQ => + yield "EILSEQ"; + case ERESTART => + yield "ERESTART"; + case ESTRPIPE => + yield "ESTRPIPE"; + case EUSERS => + yield "EUSERS"; + case ENOTSOCK => + yield "ENOTSOCK"; + case EDESTADDRREQ => + yield "EDESTADDRREQ"; + case EMSGSIZE => + yield "EMSGSIZE"; + case EPROTOTYPE => + yield "EPROTOTYPE"; + case ENOPROTOOPT => + yield "ENOPROTOOPT"; + case EPROTONOSUPPORT => + yield "EPROTONOSUPPORT"; + case ESOCKTNOSUPPORT => + yield "ESOCKTNOSUPPORT"; + case EOPNOTSUPP => + yield "EOPNOTSUPP"; + case EPFNOSUPPORT => + yield "EPFNOSUPPORT"; + case EAFNOSUPPORT => + yield "EAFNOSUPPORT"; + case EADDRINUSE => + yield "EADDRINUSE"; + case EADDRNOTAVAIL => + yield "EADDRNOTAVAIL"; + case ENETDOWN => + yield "ENETDOWN"; + case ENETUNREACH => + yield "ENETUNREACH"; + case ENETRESET => + yield "ENETRESET"; + case ECONNABORTED => + yield "ECONNABORTED"; + case ECONNRESET => + yield "ECONNRESET"; + case ENOBUFS => + yield "ENOBUFS"; + case EISCONN => + yield "EISCONN"; + case ENOTCONN => + yield "ENOTCONN"; + case ESHUTDOWN => + yield "ESHUTDOWN"; + case ETOOMANYREFS => + yield "ETOOMANYREFS"; + case ETIMEDOUT => + yield "ETIMEDOUT"; + case ECONNREFUSED => + yield "ECONNREFUSED"; + case EHOSTDOWN => + yield "EHOSTDOWN"; + case EHOSTUNREACH => + yield "EHOSTUNREACH"; + case EALREADY => + yield "EALREADY"; + case EINPROGRESS => + yield "EINPROGRESS"; + case ESTALE => + yield "ESTALE"; + case EUCLEAN => + yield "EUCLEAN"; + case ENOTNAM => + yield "ENOTNAM"; + case ENAVAIL => + yield "ENAVAIL"; + case EISNAM => + yield "EISNAM"; + case EREMOTEIO => + yield "EREMOTEIO"; + case EDQUOT => + yield "EDQUOT"; + case ENOMEDIUM => + yield "ENOMEDIUM"; + case EMEDIUMTYPE => + yield "EMEDIUMTYPE"; + case ECANCELED => + yield "ECANCELED"; + case ENOKEY => + yield "ENOKEY"; + case EKEYEXPIRED => + yield "EKEYEXPIRED"; + case EKEYREVOKED => + yield "EKEYREVOKED"; + case EKEYREJECTED => + yield "EKEYREJECTED"; + case EOWNERDEAD => + yield "EOWNERDEAD"; + case ENOTRECOVERABLE => + yield "ENOTRECOVERABLE"; + case ERFKILL => + yield "ERFKILL"; + case EHWPOISON => + yield "EHWPOISON"; + case => + yield "[unknown errno]"; // TODO: snprintf to add errno? }; }; diff --git a/rt/+linux/segmalloc.ha b/rt/+linux/segmalloc.ha @@ -1,21 +1,21 @@ // Allocates a segment. fn segmalloc(n: size) nullable *void = { - return match (mmap(null, n, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, -1, 0)) { - err: errno => { - assert(err == ENOMEM: errno); - yield null; - }, - p: *void => p, + return match (mmap(null, n, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0)) { + case err: errno => + assert(err == ENOMEM: errno); + yield null; + case p: *void => + yield p; }; }; // Frees a segment allocated with segmalloc. fn segfree(p: *void, s: size) void = { match (munmap(p, s)) { - err: errno => abort("munmap failed"), - void => void, + case err: errno => + abort("munmap failed"); + case void => void; }; }; diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha @@ -12,16 +12,17 @@ let pathbuf: [PATH_MAX + 1]u8 = [0...]; fn copy_kpath(path: path, buf: []u8) (*const char | errno) = { let path = match (path) { - c: *const char => return c, - s: str => { - let ptr = &s: *struct { - buf: *[*]u8, - length: size, - capacity: size, - }; - yield ptr.buf[..ptr.length]; - }, - b: []u8 => b, + case c: *const char => + return c; + case s: str => + let ptr = &s: *struct { + buf: *[*]u8, + length: size, + capacity: size, + }; + yield ptr.buf[..ptr.length]; + case b: []u8 => + yield b; }; if (len(path) + 1 >= len(pathbuf)) { return ENAMETOOLONG; @@ -186,10 +187,12 @@ export fn execveat(dirfd: int, path: path, argv: *[*]nullable *const char, envp: *[*]nullable *const char, flags: int) errno = { let path = kpath(path)?; return match (wrap_return(syscall5(SYS_execveat, dirfd: u64, - path: uintptr: u64, argv: uintptr: u64, - envp: uintptr: u64, flags: u64))) { - err: errno => err, - u64 => abort("unreachable"), + path: uintptr: u64, argv: uintptr: u64, + envp: uintptr: u64, flags: u64))) { + case err: errno => + yield err; + case u64 => + abort("unreachable"); }; }; @@ -241,17 +244,17 @@ export fn mmap( let r = syscall6(SYS_mmap, addr: uintptr: u64, length: u64, prot: u64, flags: u64, fd: u64, offs: u64); return match (wrap_return(r)) { - err: errno => { - // XXX: Type promotion would simplify this - return if (r: int == -EPERM - && addr: uintptr == null: uintptr - && (flags & MAP_ANON) > 0 - && (flags & MAP_FIXED) == 0) { - // Fix up incorrect EPERM from kernel: - yield wrap_errno(ENOMEM); - } else err; - }, - n: u64 => n: uintptr: *void, + case err: errno => + // XXX: Type promotion would simplify this + return if (r: int == -EPERM + && addr: uintptr == null: uintptr + && (flags & MAP_ANON) > 0 + && (flags & MAP_FIXED) == 0) { + // Fix up incorrect EPERM from kernel: + yield wrap_errno(ENOMEM); + } else err; + case n: u64 => + yield n: uintptr: *void; }; }; @@ -276,15 +279,17 @@ export fn lseek(fd: int, off: i64, whence: uint) (i64 | errno) = { fn faccessat1(dirfd: int, path: *const char, mode: int) (bool | errno) = { return match (wrap_return(syscall3(SYS_faccessat, dirfd: u64, - path: uintptr: u64, mode: u64))) { - err: errno => switch (err) { - EACCES => false, - * => err, - }, - n: u64 => { - assert(n == 0); - yield true; - }, + path: uintptr: u64, mode: u64))) { + case err: errno => + yield switch (err) { + case EACCES => + yield false; + case => + yield err; + }; + case n: u64 => + assert(n == 0); + yield true; }; }; @@ -298,19 +303,24 @@ export fn faccessat( flags: int, ) (bool | errno) = { let path = kpath(path)?; - return match (wrap_return(syscall4(SYS_faccessat2, dirfd: u64, + match (wrap_return(syscall4(SYS_faccessat2, dirfd: u64, path: uintptr: u64, mode: u64, flags: u64))) { - err: errno => switch (err) { - EACCES => false, - ENOSYS => - if (flags == 0) faccessat1(dirfd, path, mode) - else err, - * => err, - }, - n: u64 => { - assert(n == 0); - yield true; - }, + case err: errno => + switch (err) { + case EACCES => + return false; + case ENOSYS => + if (flags == 0) { + return faccessat1(dirfd, path, mode); + } else { + return err; + }; + case => + return err; + }; + case n: u64 => + assert(n == 0); + return true; }; }; @@ -330,11 +340,16 @@ export type fcntl_arg = (void | int | *st_flock | *f_owner_ex | *u64); export fn fcntl(fd: int, cmd: int, arg: fcntl_arg) (int | errno) = { let _fd = fd: u64, _cmd = cmd: u64; return wrap_return(match (arg) { - void => syscall2(SYS_fcntl, _fd, _cmd), - i: int => syscall3(SYS_fcntl, _fd, _cmd, i: u64), - l: *st_flock => syscall3(SYS_fcntl, _fd, _cmd, l: uintptr: u64), - o: *f_owner_ex => syscall3(SYS_fcntl, _fd, _cmd, o: uintptr: u64), - u: *u64 => syscall3(SYS_fcntl, _fd, _cmd, u: uintptr: u64), + case void => + yield syscall2(SYS_fcntl, _fd, _cmd); + case i: int => + yield syscall3(SYS_fcntl, _fd, _cmd, i: u64); + case l: *st_flock => + yield syscall3(SYS_fcntl, _fd, _cmd, l: uintptr: u64); + case o: *f_owner_ex => + yield syscall3(SYS_fcntl, _fd, _cmd, o: uintptr: u64); + case u: *u64 => + yield syscall3(SYS_fcntl, _fd, _cmd, u: uintptr: u64); })?: int; }; diff --git a/rt/+x86_64/backtrace.ha b/rt/+x86_64/backtrace.ha @@ -13,8 +13,10 @@ export fn backtrace() frame = frame { // Returns the frame above the current frame, if any. export fn nextframe(sframe: frame) (frame | void) = { let addr = sframe.addr: *nullable *void; - return match (*addr) { - null => void, - a: *void => frame { addr = a } + match (*addr) { + case a: *void => + return frame { addr = a }; + case null => + return; }; }; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -800,12 +800,6 @@ types() { gen_ssa types } -unicode() { - gen_srcs unicode \ - properties.ha - gen_ssa unicode -} - unix() { # XXX: getuid and setuid are probably platform-specific too gen_srcs unix \ @@ -922,7 +916,6 @@ strio temp time types -unicode unix unix::hosts unix::passwd diff --git a/sort/+test.ha b/sort/+test.ha @@ -8,8 +8,10 @@ fn ncmp(a: const *void, b: const *void) int = { for (let i = 0z; i < len(nums); i += 1) { const key = nums[i]; match (search(nums[..], size(int), &key, &ncmp): nullable *int) { - null => abort(), - p: *int => assert(p == &nums[i] && *p == nums[i]), + case null => + abort(); + case p: *int => + assert(p == &nums[i] && *p == nums[i]); }; }; const key = 1337; diff --git a/stdlib.mk b/stdlib.mk @@ -154,6 +154,10 @@ hare_stdlib_deps+=$(stdlib_errors) stdlib_fmt=$(HARECACHE)/fmt/fmt.o hare_stdlib_deps+=$(stdlib_fmt) +# gen_lib fnmatch +stdlib_fnmatch=$(HARECACHE)/fnmatch/fnmatch.o +hare_stdlib_deps+=$(stdlib_fnmatch) + # gen_lib format::elf stdlib_format_elf=$(HARECACHE)/format/elf/format_elf.o hare_stdlib_deps+=$(stdlib_format_elf) @@ -162,10 +166,6 @@ hare_stdlib_deps+=$(stdlib_format_elf) stdlib_format_xml=$(HARECACHE)/format/xml/format_xml.o hare_stdlib_deps+=$(stdlib_format_xml) -# gen_lib fnmatch -stdlib_fnmatch=$(HARECACHE)/fnmatch/fnmatch.o -hare_stdlib_deps+=$(stdlib_fnmatch) - # gen_lib fs stdlib_fs=$(HARECACHE)/fs/fs.o hare_stdlib_deps+=$(stdlib_fs) @@ -330,10 +330,6 @@ hare_stdlib_deps+=$(stdlib_time) stdlib_types=$(HARECACHE)/types/types.o hare_stdlib_deps+=$(stdlib_types) -# gen_lib unicode -stdlib_unicode=$(HARECACHE)/unicode/unicode.o -hare_stdlib_deps+=$(stdlib_unicode) - # gen_lib unix stdlib_unix=$(HARECACHE)/unix/unix.o hare_stdlib_deps+=$(stdlib_unix) @@ -571,6 +567,16 @@ $(HARECACHE)/fmt/fmt.ssa: $(stdlib_fmt_srcs) $(stdlib_rt) $(stdlib_bufio) $(stdl @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nfmt \ -t$(HARECACHE)/fmt/fmt.td $(stdlib_fmt_srcs) +# fnmatch +stdlib_fnmatch_srcs= \ + $(STDLIB)/fnmatch/fnmatch.ha + +$(HARECACHE)/fnmatch/fnmatch.ssa: $(stdlib_fnmatch_srcs) $(stdlib_rt) $(stdlib_strings) $(stdlib_bytes) $(stdlib_sort) $(stdlib_ascii) $(stdlib_io) $(stdlib_fmt) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/fnmatch + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nfnmatch \ + -t$(HARECACHE)/fnmatch/fnmatch.td $(stdlib_fnmatch_srcs) + # format::elf stdlib_format_elf_srcs= \ $(STDLIB)/format/elf/$(ARCH).ha \ @@ -595,16 +601,6 @@ $(HARECACHE)/format/xml/format_xml.ssa: $(stdlib_format_xml_srcs) $(stdlib_rt) $ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nformat::xml \ -t$(HARECACHE)/format/xml/format_xml.td $(stdlib_format_xml_srcs) -# fnmatch -stdlib_fnmatch_srcs= \ - $(STDLIB)/fnmatch/fnmatch.ha - -$(HARECACHE)/fnmatch/fnmatch.ssa: $(stdlib_fnmatch_srcs) $(stdlib_rt) $(stdlib_strings) $(stdlib_bytes) $(stdlib_sort) $(stdlib_ascii) $(stdlib_io) $(stdlib_fmt) - @printf 'HAREC \t$@\n' - @mkdir -p $(HARECACHE)/fnmatch - @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nfnmatch \ - -t$(HARECACHE)/fnmatch/fnmatch.td $(stdlib_fnmatch_srcs) - # fs stdlib_fs_srcs= \ $(STDLIB)/fs/types.ha \ @@ -1136,16 +1132,6 @@ $(HARECACHE)/types/types.ssa: $(stdlib_types_srcs) $(stdlib_rt) @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ntypes \ -t$(HARECACHE)/types/types.td $(stdlib_types_srcs) -# unicode -stdlib_unicode_srcs= \ - $(STDLIB)/unicode/properties.ha - -$(HARECACHE)/unicode/unicode.ssa: $(stdlib_unicode_srcs) $(stdlib_rt) - @printf 'HAREC \t$@\n' - @mkdir -p $(HARECACHE)/unicode - @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nunicode \ - -t$(HARECACHE)/unicode/unicode.td $(stdlib_unicode_srcs) - # unix stdlib_unix_srcs= \ $(STDLIB)/unix/$(PLATFORM)/nice.ha \ @@ -1381,6 +1367,10 @@ hare_testlib_deps+=$(testlib_errors) testlib_fmt=$(TESTCACHE)/fmt/fmt.o hare_testlib_deps+=$(testlib_fmt) +# gen_lib fnmatch +testlib_fnmatch=$(TESTCACHE)/fnmatch/fnmatch.o +hare_testlib_deps+=$(testlib_fnmatch) + # gen_lib format::elf testlib_format_elf=$(TESTCACHE)/format/elf/format_elf.o hare_testlib_deps+=$(testlib_format_elf) @@ -1389,10 +1379,6 @@ hare_testlib_deps+=$(testlib_format_elf) testlib_format_xml=$(TESTCACHE)/format/xml/format_xml.o hare_testlib_deps+=$(testlib_format_xml) -# gen_lib fnmatch -testlib_fnmatch=$(TESTCACHE)/fnmatch/fnmatch.o -hare_testlib_deps+=$(testlib_fnmatch) - # gen_lib fs testlib_fs=$(TESTCACHE)/fs/fs.o hare_testlib_deps+=$(testlib_fs) @@ -1557,10 +1543,6 @@ hare_testlib_deps+=$(testlib_time) testlib_types=$(TESTCACHE)/types/types.o hare_testlib_deps+=$(testlib_types) -# gen_lib unicode -testlib_unicode=$(TESTCACHE)/unicode/unicode.o -hare_testlib_deps+=$(testlib_unicode) - # gen_lib unix testlib_unix=$(TESTCACHE)/unix/unix.o hare_testlib_deps+=$(testlib_unix) @@ -1805,6 +1787,17 @@ $(TESTCACHE)/fmt/fmt.ssa: $(testlib_fmt_srcs) $(testlib_rt) $(testlib_bufio) $(t @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nfmt \ -t$(TESTCACHE)/fmt/fmt.td $(testlib_fmt_srcs) +# fnmatch +testlib_fnmatch_srcs= \ + $(STDLIB)/fnmatch/fnmatch.ha \ + $(STDLIB)/fnmatch/+test.ha + +$(TESTCACHE)/fnmatch/fnmatch.ssa: $(testlib_fnmatch_srcs) $(testlib_rt) $(testlib_strings) $(testlib_bytes) $(testlib_sort) $(testlib_ascii) $(testlib_io) $(testlib_fmt) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/fnmatch + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nfnmatch \ + -t$(TESTCACHE)/fnmatch/fnmatch.td $(testlib_fnmatch_srcs) + # format::elf testlib_format_elf_srcs= \ $(STDLIB)/format/elf/$(ARCH).ha \ @@ -1830,17 +1823,6 @@ $(TESTCACHE)/format/xml/format_xml.ssa: $(testlib_format_xml_srcs) $(testlib_rt) @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nformat::xml \ -t$(TESTCACHE)/format/xml/format_xml.td $(testlib_format_xml_srcs) -# fnmatch -testlib_fnmatch_srcs= \ - $(STDLIB)/fnmatch/fnmatch.ha \ - $(STDLIB)/fnmatch/+test.ha - -$(TESTCACHE)/fnmatch/fnmatch.ssa: $(testlib_fnmatch_srcs) $(testlib_rt) $(testlib_strings) $(testlib_bytes) $(testlib_sort) $(testlib_ascii) $(testlib_io) $(testlib_fmt) - @printf 'HAREC \t$@\n' - @mkdir -p $(TESTCACHE)/fnmatch - @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nfnmatch \ - -t$(TESTCACHE)/fnmatch/fnmatch.td $(testlib_fnmatch_srcs) - # fs testlib_fs_srcs= \ $(STDLIB)/fs/types.ha \ @@ -2389,16 +2371,6 @@ $(TESTCACHE)/types/types.ssa: $(testlib_types_srcs) $(testlib_rt) @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ntypes \ -t$(TESTCACHE)/types/types.td $(testlib_types_srcs) -# unicode -testlib_unicode_srcs= \ - $(STDLIB)/unicode/properties.ha - -$(TESTCACHE)/unicode/unicode.ssa: $(testlib_unicode_srcs) $(testlib_rt) - @printf 'HAREC \t$@\n' - @mkdir -p $(TESTCACHE)/unicode - @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nunicode \ - -t$(TESTCACHE)/unicode/unicode.td $(testlib_unicode_srcs) - # unix testlib_unix_srcs= \ $(STDLIB)/unix/$(PLATFORM)/nice.ha \ diff --git a/strconv/numeric.ha b/strconv/numeric.ha @@ -4,12 +4,17 @@ use types; // statically allocated and will be overwritten on subsequent calls; see // [[strings::dup]] to duplicate the result. export fn signedtosb(n: types::signed, b: base) const str = { - return match (n) { - i: int => itosb(i, b), - i: i8 => i8tosb(i, b), - i: i16 => i16tosb(i, b), - i: i32 => i32tosb(i, b), - i: i64 => i64tosb(i, b), + match (n) { + case i: int => + return itosb(i, b); + case i: i8 => + return i8tosb(i, b); + case i: i16 => + return i16tosb(i, b); + case i: i32 => + return i32tosb(i, b); + case i: i64 => + return i64tosb(i, b); }; }; @@ -22,13 +27,19 @@ export fn signedtos(n: types::signed) const str = signedtosb(n, base::DEC); // is statically allocated and will be overwritten on subsequent calls; see // [[strings::dup]] to duplicate the result. export fn unsignedtosb(n: types::unsigned, b: base) const str = { - return match (n) { - u: size => ztosb(u, b), - u: uint => utosb(u, b), - u: u8 => u8tosb(u, b), - u: u16 => u16tosb(u, b), - u: u32 => u32tosb(u, b), - u: u64 => u64tosb(u, b), + match (n) { + case u: size => + return ztosb(u, b); + case u: uint => + return utosb(u, b); + case u: u8 => + return u8tosb(u, b); + case u: u16 => + return u16tosb(u, b); + case u: u32 => + return u32tosb(u, b); + case u: u64 => + return u64tosb(u, b); }; }; @@ -41,9 +52,11 @@ export fn unsignedtos(n: types::unsigned) const str = unsignedtosb(n, base::DEC) // 8, 10, or 16. The return value is statically allocated and will be // overwritten on subsequent calls; see [[strings::dup]] to duplicate the result. export fn integertosb(n: types::integer, b: base) const str = { - return match (n) { - s: types::signed => signedtosb(s, b), - u: types::unsigned => unsignedtosb(u, b), + match (n) { + case s: types::signed => + return signedtosb(s, b); + case u: types::unsigned => + return unsignedtosb(u, b); }; }; @@ -57,9 +70,11 @@ export fn integertos(n: types::integer) const str = integertosb(n, base::DEC); // [[strings::dup]] to duplicate the result. export fn floatingtosb(n: types::floating, b: base) const str = { assert(b == base::DEC); - return match (n) { - f: f32 => abort(), // TODO - f: f64 => f64tos(f), + match (n) { + case f: f32 => + abort(); // TODO + case f: f64 => + return f64tos(f); }; }; @@ -72,9 +87,11 @@ export fn floatingtos(n: types::floating) const str = floatingtosb(n, base::DEC) // is statically allocated and will be overwritten on subsequent calls; see // [[strings::dup]] to duplicate the result. export fn numerictosb(n: types::numeric, b: base) const str = { - return match (n) { - i: types::integer => integertosb(i, b), - f: types::floating => floatingtosb(f, b), + match (n) { + case i: types::integer => + return integertosb(i, b); + case f: types::floating => + return floatingtosb(f, b); }; }; diff --git a/strconv/stou.ha b/strconv/stou.ha @@ -27,16 +27,22 @@ export fn stou64b(s: str, base: uint) (u64 | invalid | overflow) = { let iter = strings::iter(s); for (true) { let r: rune = match (strings::next(&iter)) { - void => break, - r: rune => r, + case r: rune => + yield r; + case void => + break; }; let digit = match (rune_to_integer(r)) { - void => return (iter.dec.offs - 1): invalid, - d: u64 => d, + case void => + return (iter.dec.offs - 1): invalid; + case d: u64 => + yield d; }; - if (digit >= base: u64) return (iter.dec.offs - 1): invalid; + if (digit >= base: u64) { + return (iter.dec.offs - 1): invalid; + }; let old = n; @@ -103,12 +109,20 @@ export fn stoub(s: str, base: uint) (uint | invalid | overflow) = { // returned. Supported bases are 2, 8, 10 and 16. export fn stozb(s: str, base: uint) (size | invalid | overflow) = { static assert(size(size) == size(u32) || size(size) == size(u64)); - return if (size(size) == size(u32)) match (stou32b(s, base)) { - v: (invalid | overflow) => v, - n: u32 => n: size, - } else match (stou64b(s, base)) { - v: (invalid | overflow) => v, - n: u64 => n: size, + if (size(size) == size(u32)) { + match (stou32b(s, base)) { + case v: (invalid | overflow) => + return v; + case n: u32 => + return n: size; + }; + } else { + match (stou64b(s, base)) { + case v: (invalid | overflow) => + return v; + case n: u64 => + return n: size; + }; }; }; diff --git a/strings/contains.ha b/strings/contains.ha @@ -3,8 +3,10 @@ use encoding::utf8; // Returns true if a string contains a rune or a sub-string. export fn contains(haystack: str, needle: (str | rune)) bool = match (needle) { - s: str => bytes::contains(toutf8(haystack), toutf8(s)), - r: rune => bytes::contains(toutf8(haystack), utf8::encoderune(r)), +case s: str => + yield bytes::contains(toutf8(haystack), toutf8(s)); +case r: rune => + yield bytes::contains(toutf8(haystack), utf8::encoderune(r)); }; @test fn contains() void = { diff --git a/strings/cstrings.ha b/strings/cstrings.ha @@ -22,8 +22,8 @@ export fn cstrlen(cstr: *const char) size = { export fn fromc_unsafe(cstr: *const char) const str = { const l = cstrlen(cstr); const s = types::string { - data = cstr: *[*]u8, - length = l, + data = cstr: *[*]u8, + length = l, capacity = l, }; return *(&s: *const str); @@ -42,12 +42,15 @@ export fn fromc(cstr: *const char) const str = { export fn to_c(s: const str) *char = { let ptr = rt::malloc(len(s) + 1): nullable *[*]u8; let ptr = match (ptr) { - null => abort("Out of memory"), - p: *[*]u8 => p, + case null => + abort("Out of memory"); + case p: *[*]u8 => + yield p; }; match ((&s: *types::string).data) { - null => void, - data: *[*]u8 => rt::memcpy(ptr, data, len(s)), + case null => void; + case data: *[*]u8 => + yield rt::memcpy(ptr, data, len(s)); }; ptr[len(s)] = 0; return ptr: *char; diff --git a/strings/dup.ha b/strings/dup.ha @@ -6,12 +6,16 @@ use types; export fn dup(s: const str) str = { const in = &s: *types::string; const id = match (in.data) { - null => return "", // Empty string - b: *[*]u8 => b, + case null => + return ""; // Empty string + case b: *[*]u8 => + yield b; }; let buf: *[*]u8 = match (rt::malloc(in.length + 1)) { - null => abort("Out of memory"), - v: *void => v, + case null => + abort("Out of memory"); + case v: *void => + yield v; }; bytes::copy(buf[..in.length + 1z], id[..in.length + 1]); let out = types::string { diff --git a/strings/index.ha b/strings/index.ha @@ -4,17 +4,25 @@ use bytes; // void if not present. The index returned is the rune-wise index, not the // byte-wise index. export fn index(haystack: str, needle: (str | rune)) (size | void) = { - return match (needle) { - r: rune => index_rune(haystack, r), - s: str => abort(), // TODO + match (needle) { + case r: rune => + return index_rune(haystack, r); + case s: str => + abort(); // TODO }; }; fn index_rune(s: str, r: rune) (size | void) = { let iter = iter(s); - for (let i = 0z; true; i += 1) match (next(&iter)) { - n: rune => if (r == n) return i, - void => break, + for (let i = 0z; true; i += 1) { + match (next(&iter)) { + case n: rune => + if (r == n) { + return i; + }; + case void => + break; + }; }; }; diff --git a/strings/iter.ha b/strings/iter.ha @@ -31,17 +31,17 @@ export fn riter(src: str) iterator = { // [[unicode::graphiter]] instead. export fn next(iter: *iterator) (rune | void) = { match (iter.push) { - r: rune => { - iter.push = void; - return r; - }, - void => void, + case r: rune => + iter.push = void; + return r; + case void => void; }; return match (utf8::next(&iter.dec)) { - r: rune => r, - void => void, - (utf8::more | utf8::invalid) => - abort("Invalid UTF-8 string (this should not happen)"), + case void => void; + case (utf8::more | utf8::invalid) => + abort("Invalid UTF-8 string (this should not happen)"); + case r: rune => + yield r; }; }; @@ -50,10 +50,12 @@ export fn next(iter: *iterator) (rune | void) = { export fn prev(iter: *iterator) (rune | void) = { assert(iter.push is void); return match (utf8::prev(&iter.dec)) { - r: rune => r, - void => void, - (utf8::more | utf8::invalid) => - abort("Invalid UTF-8 string (this should not happen)"), + case void => + yield void; + case (utf8::more | utf8::invalid) => + abort("Invalid UTF-8 string (this should not happen)"); + case r: rune => + yield r; }; }; @@ -79,8 +81,10 @@ export fn iter_str(iter: *iterator) str = { const expected1 = ['こ', 'ん']; for (let i = 0z; i < len(expected1); i += 1) { match (next(&s)) { - r: rune => assert(r == expected1[i]), - void => abort(), + case r: rune => + assert(r == expected1[i]); + case void => + abort(); }; }; assert(iter_str(&s) == "にちは"); @@ -88,8 +92,10 @@ export fn iter_str(iter: *iterator) str = { const expected2 = ['ん', 'に', 'ち', 'は']; for (let i = 0z; i < len(expected2); i += 1) { match (next(&s)) { - r: rune => assert(r == expected2[i]), - void => abort(), + case r: rune => + assert(r == expected2[i]); + case void => + abort(); }; }; assert(next(&s) is void); @@ -102,8 +108,10 @@ export fn iter_str(iter: *iterator) str = { const expected3 = ['は', 'ち', 'に']; for (let i = 0z; i< len(expected3); i += 1) { match (prev(&s)) { - r: rune => assert(r == expected3[i]), - void => abort(), + case r: rune => + assert(r == expected3[i]); + case void => + abort(); }; }; assert(prev(&s) is void); diff --git a/strings/sub.ha b/strings/sub.ha @@ -5,9 +5,11 @@ export type end = void; fn utf8_byte_len_bounded(iter: *iterator, end: size) size = { let pos = 0z; for (let i = 0z; i < end; i += 1) { - let r: rune = match (strings::next(iter)) { - void => break, - r: rune => r, + let r = match (strings::next(iter)) { + case r: rune => + yield r; + case void => + break; }; pos += utf8::runesz(r); @@ -18,9 +20,11 @@ fn utf8_byte_len_bounded(iter: *iterator, end: size) size = { fn utf8_byte_len_unbounded(iter: *iterator) size = { let pos = 0z; for (true) { - let r: rune = match (strings::next(iter)) { - void => break, - r: rune => r, + let r = match (strings::next(iter)) { + case r: rune => + yield r; + case void => + break; }; pos += utf8::runesz(r); @@ -40,8 +44,10 @@ export fn sub(s: str, start: size, end: (size | end)) str = { let iter = iter(s); let starti = utf8_byte_len_bounded(&iter, start); let endi = match (end) { - sz: size => starti + utf8_byte_len_bounded(&iter, sz - start), - end => starti + utf8_byte_len_unbounded(&iter), + case sz: size => + yield starti + utf8_byte_len_bounded(&iter, sz - start); + case end => + yield starti + utf8_byte_len_unbounded(&iter); }; let bytes = toutf8(s); return fromutf8_unsafe(bytes[starti..endi]); diff --git a/strings/tokenize.ha b/strings/tokenize.ha @@ -18,16 +18,18 @@ export fn tokenize(s: str, delim: str) tokenizer = // void if there are no tokens left. export fn next_token(s: *tokenizer) (str | void) = { return match (bytes::next_token(s)) { - b: []u8 => fromutf8(b), - void => void, + case b: []u8 => + yield fromutf8(b); + case void => void; }; }; // Same as next_token(), but does not advance the cursor export fn peek_token(s: *tokenizer) (str | void) = { return match (bytes::peek_token(s)) { - b: []u8 => fromutf8(b), - void => void, + case b: []u8 => + yield fromutf8(b); + case void => void; }; }; @@ -40,24 +42,32 @@ export fn remaining_tokens(s: *tokenizer) str = { @test fn tokenize() void = { let tok = tokenize("Hello, my name is drew", " "); match (next_token(&tok)) { - s: str => assert(s == "Hello,"), - void => abort(), + case s: str => + assert(s == "Hello,"); + case void => + abort(); }; match (next_token(&tok)) { - s: str => assert(s == "my"), - void => abort(), + case s: str => + assert(s == "my"); + case void => + abort(); }; match (peek_token(&tok)) { - s: str => assert(s == "name"), - void => abort(), + case s: str => + assert(s == "name"); + case void => + abort(); }; match (next_token(&tok)) { - s: str => assert(s == "name"), - void => abort(), + case s: str => + assert(s == "name"); + case void => + abort(); }; assert(remaining_tokens(&tok) == "is drew"); @@ -89,8 +99,10 @@ export fn splitN(in: str, delim: str, n: size) []str = { let tok = tokenize(in, delim); for (let i = 0z; i < n - 1z; i += 1) { match (next_token(&tok)) { - s: str => append(toks, s), - void => return toks, + case s: str => + append(toks, s); + case void => + return toks; }; }; append(toks, remaining_tokens(&tok)); diff --git a/strings/trim.ha b/strings/trim.ha @@ -12,8 +12,10 @@ export fn ltrim(input: str, trim: rune...) str = { let it = iter(input); for (true) { const r = match (next(&it)) { - r: rune => r, - void => break, + case r: rune => + yield r; + case void => + break; }; let found = false; for (let i = 0z; i < len(trim); i += 1) { @@ -40,8 +42,10 @@ export fn rtrim(input: str, trim: rune...) str = { let it = riter(input); for (true) { const r = match (prev(&it)) { - r: rune => r, - void => break, + case r: rune => + yield r; + case void => + break; }; let found = false; for (let i = 0z; i < len(trim); i += 1) { diff --git a/temp/+linux.ha b/temp/+linux.ha @@ -32,11 +32,12 @@ export fn file( } else { oflags |= fs::flags::WRONLY; }; - return match (os::create(get_tmpdir(), fmode, oflags)) { - // TODO: Add a custom "close" function which removes the named - // file - err: fs::error => named(os::cwd, get_tmpdir(), iomode, mode...), - f: io::file => f, + // TODO: Add a custom "close" function which removes the named file + match (os::create(get_tmpdir(), fmode, oflags)) { + case err: fs::error => + return named(os::cwd, get_tmpdir(), iomode, mode...); + case f: io::file => + return f; }; }; @@ -74,9 +75,12 @@ export fn named( const id = *(&rand[0]: *u64); const fpath = fmt::bsprintf(pathbuf, "{}/temp.{}", path, id); match (fs::create_file(fs, fpath, fmode, oflags)) { - errors::exists => continue, - err: fs::error => return err, - f: io::file => return f, + case errors::exists => + continue; + case err: fs::error => + return err; + case f: io::file => + return f; }; }; abort(); // Unreachable @@ -99,8 +103,8 @@ export fn dir() str = { let path = path::join(get_tmpdir(), name); match (os::mkdir(path)) { - err: fs::error => abort("Could not create temp directory"), - void => void, + case err: fs::error => abort("Could not create temp directory"); + case void => void; }; return path; }; diff --git a/time/+linux/functions.ha b/time/+linux/functions.ha @@ -31,13 +31,15 @@ export fn sleep(n: duration) void = { for (true) { let res = rt::timespec { ... }; match (rt::nanosleep(req, &res)) { - void => return, - err: rt::errno => switch (err) { - rt::EINTR => { - req = &res; - }, - * => abort("Unexpected error from nanosleep"), - }, + case void => + return; + case err: rt::errno => + switch (err) { + case rt::EINTR => + req = &res; + case => + abort("Unexpected error from nanosleep"); + }; }; }; }; @@ -80,8 +82,10 @@ fn cgt_vdso() nullable *fn(_: int, _: *rt::timespec) int = { fn now_vdso(clock: clock, tp: *rt::timespec) (void | rt::errno) = { const vfn = match (cgt_vdso()) { - null => return rt::wrap_errno(rt::ENOSYS), - vfn: *fn(_: int, _: *rt::timespec) int => vfn, + case null => + return rt::wrap_errno(rt::ENOSYS); + case vfn: *fn(_: int, _: *rt::timespec) int => + yield vfn; }; const ret = vfn(clock, tp); if (ret == 0) { @@ -94,14 +98,18 @@ fn now_vdso(clock: clock, tp: *rt::timespec) (void | rt::errno) = { export fn now(clock: clock) instant = { let tp = rt::timespec { ... }; let err = match (now_vdso(clock, &tp)) { - void => return timespec_to_instant(tp), - err: rt::errno => err + case void => + return timespec_to_instant(tp); + case err: rt::errno => + yield err; }; if (err != rt::wrap_errno(rt::ENOSYS)) { abort("Unexpected error from clock_gettime"); }; - return match (rt::clock_gettime(clock, &tp)) { - void => timespec_to_instant(tp), - err: rt::errno => abort("Unexpected error from clock_gettime"), + match (rt::clock_gettime(clock, &tp)) { + case void => + return timespec_to_instant(tp); + case err: rt::errno => + abort("Unexpected error from clock_gettime"); }; }; diff --git a/unicode/README b/unicode/README @@ -1,30 +0,0 @@ -This module provides Unicode support for Hare programs. - -Programs which deal with basic text manipulation are likely to be served -sufficiently by the [[encoding::utf8]], [[strings]], [[ascii]], and so on. For -example, the question of "is this character uppercase?" is often sufficiently -answered with [[ascii::isupper]], and matters such as Unicode string equivalence -are often fraught with error potential - for example, a vulnerability was once -found in a web login form which used a Unicode equivalence comparison on -usernames, allowing a malicious actor to register a username which was bytewise -distinct but uniwise equal to a victim, and then use it to log into their -account. This module also contains a copy of the Unicode Character Database, -which is rather large, and linking to it will increase the size of your -binaries. - -The purpose of this module is not to handle every day string manipulation, but -instead to provide support code for software which is explicitly aware of -internationalization concerns and seeking out functions which specifically -address those concerns. - -This module makes little attempt to be useful without a broader understanding of -Unicode. The module is close to a 1:1 implementation of the Unicode standard, -and it is recommended that any reading of this module's API or source code is -accompanied by a reading of the Unicode standard. The documentation for each -type and function makes an effort to direct the reader to the appropriate part -of the Unicode standard. - -See the [[i18n]] module for a high-level internationalization API. - -The present implementation of this module conforms to Unicode 13.0.0, which was -released on March 11th, 2020. diff --git a/unicode/properties.ha b/unicode/properties.ha @@ -1,1130 +0,0 @@ -// Unicode character blocks. See Blocks.txt in the UCD. -export type blk = enum { - ADLAM, - AEGEAN_NUMBERS, - AHOM, - ALCHEMICAL, - ALPHABETIC_PF, - ANATOLIAN_HIEROGLYPHS, - ANCIENT_GREEK_MUSIC, - ANCIENT_GREEK_NUMBERS, - ANCIENT_SYMBOLS, - ARABIC, - ARABIC_EXT_A, - ARABIC_MATH, - ARABIC_PF_A, - ARABIC_PF_B, - ARABIC_SUP, - ARMENIAN, - ARROWS, - ASCII, - AVESTAN, - BALINESE, - BAMUM, - BAMUM_SUP, - BASSA_VAH, - BATAK, - BENGALI, - BHAIKSUKI, - BLOCK_ELEMENTS, - BOPOMOFO, - BOPOMOFO_EXT, - BOX_DRAWING, - BRAHMI, - BRAILLE, - BUGINESE, - BUHID, - BYZANTINE_MUSIC, - CARIAN, - CAUCASIAN_ALBANIAN, - CHAKMA, - CHAM, - CHEROKEE, - CHEROKEE_SUP, - CHESS_SYMBOLS, - CHORASMIAN, - CJK, - CJK_COMPAT, - CJK_COMPAT_FORMS, - CJK_COMPAT_IDEOGRAPHS, - CJK_COMPAT_IDEOGRAPHS_SUP, - CJK_EXT_A, - CJK_EXT_B, - CJK_EXT_C, - CJK_EXT_D, - CJK_EXT_E, - CJK_EXT_F, - CJK_EXT_G, - CJK_RADICALS_SUP, - CJK_STROKES, - CJK_SYMBOLS, - COMPAT_JAMO, - CONTROL_PICTURES, - COPTIC, - COPTIC_EPACT_NUMBERS, - COUNTING_ROD, - CUNEIFORM, - CUNEIFORM_NUMBERS, - CURRENCY_SYMBOLS, - CYPRIOT_SYLLABARY, - CYRILLIC, - CYRILLIC_EXT_A, - CYRILLIC_EXT_B, - CYRILLIC_EXT_C, - CYRILLIC_SUP, - DESERET, - DEVANAGARI, - DEVANAGARI_EXT, - DIACRITICALS, - DIACRITICALS_FOR_SYMBOLS, - DIACRITICALS_SUP, - DIACRITICALS_EXT, - DINGBATS, - DIVES_AKURU, - DOGRA, - DOMINO, - DUPLOYAN, - EARLY_DYNASTIC_CUNEIFORM, - EGYPTIAN_HIEROGLYPHS, - EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS, - ELBASAN, - ELYMAIC, - EMOTICONS, - ENCLOSED_ALPHANUM, - ENCLOSED_ALPHANUM_SUP, - ENCLOSED_CJK, - ENCLOSED_IDEOGRAPHIC_SUP, - ETHIOPIC, - ETHIOPIC_EXT, - ETHIOPIC_EXT_A, - ETHIOPIC_SUP, - GEOMETRIC_SHAPES, - GEOMETRIC_SHAPES_EXT, - GEORGIAN, - GEORGIAN_EXT, - GEORGIAN_SUP, - GLAGOLITIC, - GLAGOLITIC_SUP, - GOTHIC, - GRANTHA, - GREEK, - GREEK_EXT, - GUJARATI, - GUNJALA_GONDI, - GURMUKHI, - HALF_AND_FULL_FORMS, - HALF_MARKS, - HANGUL, - HANIFI_ROHINGYA, - HANUNOO, - HATRAN, - HEBREW, - HIGH_PU_SURROGATES, - HIGH_SURROGATES, - HIRAGANA, - IDC, - IDEOGRAPHIC_SYMBOLS, - IMPERIAL_ARAMAIC, - INDIC_NUMBER_FORMS, - INDIC_SIYAQ_NUMBERS, - INSCRIPTIONAL_PAHLAVI, - INSCRIPTIONAL_PARTHIAN, - IPA_EXT, - JAMO, - JAMO_EXT_A, - JAMO_EXT_B, - JAVANESE, - KAITHI, - KANA_EXT_A, - KANA_SUP, - KANBUN, - KANGXI, - KANNADA, - KATAKANA, - KATAKANA_EXT, - KAYAH_LI, - KHAROSHTHI, - KHITAN_SMALL_SCRIPT, - KHMER, - KHMER_SYMBOLS, - KHOJKI, - KHUDAWADI, - LAO, - LATIN_1_SUP, - LATIN_EXT_A, - LATIN_EXT_ADDITIONAL, - LATIN_EXT_B, - LATIN_EXT_C, - LATIN_EXT_D, - LATIN_EXT_E, - LEPCHA, - LETTERLIKE_SYMBOLS, - LIMBU, - LINEAR_A, - LINEAR_B_IDEOGRAMS, - LINEAR_B_SYLLABARY, - LISU, - LISU_SUP, - LOW_SURROGATES, - LYCIAN, - LYDIAN, - MAHAJANI, - MAHJONG, - MAKASAR, - MALAYALAM, - MANDAIC, - MANICHAEAN, - MARCHEN, - MASARAM_GONDI, - MATH_ALPHANUM, - MATH_OPERATORS, - MAYAN_NUMERALS, - MEDEFAIDRIN, - MEETEI_MAYEK, - MEETEI_MAYEK_EXT, - MENDE_KIKAKUI, - MEROITIC_CURSIVE, - MEROITIC_HIEROGLYPHS, - MIAO, - MISC_ARROWS, - MISC_MATH_SYMBOLS_A, - MISC_MATH_SYMBOLS_B, - MISC_PICTOGRAPHS, - MISC_SYMBOLS, - MISC_TECHNICAL, - MODI, - MODIFIER_LETTERS, - MODIFIER_TONE_LETTERS, - MONGOLIAN, - MONGOLIAN_SUP, - MRO, - MUSIC, - MULTANI, - MYANMAR, - MYANMAR_EXT_A, - MYANMAR_EXT_B, - NABATAEAN, - NANDINAGARI, - NB, - NEW_TAI_LUE, - NEWA, - NKO, - NUMBER_FORMS, - NUSHU, - NYIAKENG_PUACHUE_HMONG, - OCR, - OGHAM, - OL_CHIKI, - OLD_HUNGARIAN, - OLD_ITALIC, - OLD_NORTH_ARABIAN, - OLD_PERMIC, - OLD_PERSIAN, - OLD_SOGDIAN, - OLD_SOUTH_ARABIAN, - OLD_TURKIC, - ORIYA, - ORNAMENTAL_DINGBATS, - OSAGE, - OSMANYA, - OTTOMAN_SIYAQ_NUMBERS, - PAHAWH_HMONG, - PALMYRENE, - PAU_CIN_HAU, - PHAGS_PA, - PHAISTOS, - PHOENICIAN, - PHONETIC_EXT, - PHONETIC_EXT_SUP, - PLAYING_CARDS, - PSALTER_PAHLAVI, - PUA, - PUNCTUATION, - REJANG, - RUMI, - RUNIC, - SAMARITAN, - SAURASHTRA, - SHARADA, - SHAVIAN, - SHORTHAND_FORMAT_CONTROLS, - SIDDHAM, - SINHALA, - SINHALA_ARCHAIC_NUMBERS, - SMALL_FORMS, - SMALL_KANA_EXT, - SOGDIAN, - SORA_SOMPENG, - SOYOMBO, - SPECIALS, - SUNDANESE, - SUNDANESE_SUP, - SUP_ARROWS_A, - SUP_ARROWS_B, - SUP_ARROWS_C, - SUP_MATH_OPERATORS, - SUP_PUA_A, - SUP_PUA_B, - SUP_PUNCTUATION, - SUP_SYMBOLS_AND_PICTOGRAPHS, - SUPER_AND_SUB, - SUTTON_SIGNWRITING, - SYLOTI_NAGRI, - SYMBOLS_AND_PICTOGRAPHS_EXT_A, - SYMBOLS_FOR_LEGACY_COMPUTING, - SYRIAC, - SYRIAC_SUP, - TAGALOG, - TAGBANWA, - TAGS, - TAI_LE, - TAI_THAM, - TAI_VIET, - TAI_XUAN_JING, - TAKRI, - TAMIL, - TAMIL_SUP, - TANGUT, - TANGUT_COMPONENTS, - TANGUT_SUP, - TELUGU, - THAANA, - THAI, - TIBETAN, - TIFINAGH, - TIRHUTA, - TRANSPORT_AND_MAP, - UCAS, - UCAS_EXT, - UGARITIC, - VAI, - VEDIC_EXT, - VERTICAL_FORMS, - VS, - VS_SUP, - WANCHO, - WARANG_CITI, - YEZIDI, - YI_RADICALS, - YI_SYLLABLES, - YIJING, - ZANABAZAR_SQUARE, -}; - -// Unicode general character categories. See Unicode section 4.5. -export type gc = enum { - // Letter, uppercase - LU, - // Letter, lowercase - LL, - // Letter, titlecase - LT, - // Letter, modifier - LM, - // Letter, other - LO, - // Mark, nonspacing - MN, - // Mark, spacing combining - MC, - // Mark, enclosing - ME, - // Number, decimal digit - ND, - // Number, letter - NL, - // Number, other - NO, - // Punctuation, connector - PC, - // Punctuation, dash - PD, - // Punctuation, open - PS, - // Punctuation, close - PE, - // Punctuation, initial quote - PI, - // Punctuation, final quote - PF, - // Punctuation, other - PO, - // Symbol, math - SM, - // Symbol, currency - SC, - // Symbol, modifier - SK, - // Symbol, other - SO, - // Separator, space - ZS, - // Separator, line - ZL, - // Separator, paragraph - ZP, - // Other, control - CC, - // Other, format - CF, - // Other, surrogate - CS, - // Other, private use - CO, - // Other, not assigned (including noncharacters) - CN, -}; - -// Bidirectional class. See UAX #9. -export type bc = enum { - // Right-to-left (Arabic) - AL, - // Arabic number - AN, - // Paragraph separator - B, - // Boundary neutral - BN, - // Common number separator - CS, - // European number - EN, - // European number separator - ES, - // Euromean number terminator - ET, - // First strong isolate - FSI, - // Left-to-right - L, - // Left-to-right embedding - LRE, - // Right-to-left isolate - LRI, - // Left-to-right override - LRO, - // Nonspacing mark - NSM, - // Other neutrals - ON, - // Pop directional format - PDF, - // Pop directional isolate - PDI, - // Right-to-left - R, - // Right-to-left embedding - RLE, - // Right-to-left isolate - RLI, - // Right-to-left override - RLO, - // Segment separator - S, - // Whitespace - WS, -}; - -// Bidi paired bracket type. See BidiBrackets.txt in the UCD. -export type bpt = enum { - // Open - O, - // Closed - C, - // None - N, -}; - -// Decomposition type. See UAX #44, section 5.7.3. -export type dt = enum { - // Canonical mapping - CAN, - // Otherwise unspecified compatibility character - COM, - // Encircled form - ENC, - // Final presentation form (Arabic) - FIN, - // Font variant (for example, a blackletter form) - FONT, - // Vulgar fraction form - FRA, - // Initial presentation form (Arabic) - INIT, - // Isolated presentation form (Arabic) - ISO, - // Medial presentation form (Arabic) - MED, - // Narrow (or hankaku) compatibility character - NAR, - // No-break version of a space or hyphen - NB, - // Small variant form (CNS compatibility) - SML, - // CJK squared font variant - SQR, - // Subscript form - SUB, - // Superscript form - SUP, - // Vertical layout presentation form - VERT, - // Wide (or zenkaku) compatibility character - WIDE, - // None - NONE, -}; - -// Normalization quick-check properties. See UAX #44, section 5.7.5. -export type quickcheck = enum uint { - NO = 0b00, - MAYBE = 0b01, - YES = 0b11, -}; - -// Numeric type. See Unicode section 4.6. -export type nt = enum { - // Non-numeric - NONE, - // Decimal - DE, - // Digit - DI, - // Numeric - NU, -}; - -// Character joining class. See Unicode section 9.2. -export type jt = enum { - // Non-joining - U, - // Join causing - C, - // Transparent - T, - // Dual joining - D, - // Left joining - L, - // Right joining - R, -}; - -// Character joining group. See Unicode section 9.2. -export type jg = enum { - AFRICAN_FEH, - AFRICAN_NOON, - AFRICAN_QAF, - AIN, - ALAPH, - ALEF, - ALEF_MAQSURAH, - BEH, - BETH, - BURUSHASKI_YEH_BARREE, - DAL, - DALATH_RISH, - E, - FARSI_YEH, - FE, - FEH, - FINAL_SEMKATH, - GAF, - GAMAL, - HAH, - HAMZA_ON_HEH_GOAL, - HE, - HEH, - HEH_GOAL, - HETH, - HANIFI_ROHINGYA_KINNA_YA, - HANIFI_ROHINGYA_PA, - KAF, - KAPH, - KHAPH, - KNOTTED_HEH, - LAM, - LAMADH, - MALAYALAM_NGA, - MALAYALAM_JA, - MALAYALAM_NYA, - MALAYALAM_TTA, - MALAYALAM_NNA, - MALAYALAM_NNNA, - MALAYALAM_BHA, - MALAYALAM_RA, - MALAYALAM_LLA, - MALAYALAM_LLLA, - MALAYALAM_SSA, - MANICHAEAN_ALEPH, - MANICHAEAN_AYIN, - MANICHAEAN_BETH, - MANICHAEAN_DALETH, - MANICHAEAN_DHAMEDH, - MANICHAEAN_FIVE, - MANICHAEAN_GIMEL, - MANICHAEAN_HETH, - MANICHAEAN_HUNDRED, - MANICHAEAN_KAPH, - MANICHAEAN_LAMEDH, - MANICHAEAN_MEM, - MANICHAEAN_NUN, - MANICHAEAN_ONE, - MANICHAEAN_PE, - MANICHAEAN_QOPH, - MANICHAEAN_RESH, - MANICHAEAN_SADHE, - MANICHAEAN_SAMEKH, - MANICHAEAN_TAW, - MANICHAEAN_TEN, - MANICHAEAN_TETH, - MANICHAEAN_THAMEDH, - MANICHAEAN_TWENTY, - MANICHAEAN_WAW, - MANICHAEAN_YODH, - MANICHAEAN_ZAYIN, - MEEM, - MIM, - NO_JOINING_GROUP, - NOON, - NUN, - NYA, - PE, - QAF, - QAPH, - REH, - REVERSED_PE, - ROHINGYA_YEH, - SAD, - SADHE, - SEEN, - SEMKATH, - SHIN, - STRAIGHT_WAW, - SWASH_KAF, - SYRIAC_WAW, - TAH, - TAW, - TEH_MARBUTA, - TEH_MARBUTA_GOAL, - TETH, - WAW, - YEH, - YEH_BARREE, - YEH_WITH_TAIL, - YUDH, - YUDH_HE, - ZAIN, - ZHAIN, -}; - -// Line breaking properties. See UAX #14. -export type lb = enum { - // Ambiguous - AI, - // Alphabetic - AL, - // Break opportunity before and after - B2, - // Break after - BA, - // Break before - BB, - // Mandatory break - BK, - // Contingent break opportunity - CB, - // Conditional Japanese starter - CJ, - // Close punctuation - CL, - // Combining mark - CM, - // Close parenthesis - CP, - // Carriage return - CR, - // Emoji base - EB, - // Emoji modifier - EM, - // Exclamation/interrogation - EX, - // Non-breaking ("glue") - GL, - // Hangul LV syllable - H2, - // Hangul LVT syllable - H3, - // Hebrew letter - HL, - // Hyphen - HY, - // Ideographic - ID, - // Inseparable - IN, - // Infix numeric separator - IS, - // Hangul L Jamo - JL, - // Hangul T Jamo - JT, - // Hangul V Jamo - JV, - // Line feed - LF, - // Next line - NL, - // Nonstarter - NS, - // Numeric - NU, - // Open punctuation - OP, - // Postfix numeric - PO, - // Prefix numeric - PR, - // Quotation - QU, - // Regional indicator - RI, - // Complex context dependent (South East Asian) - SA, - // Surrogate - SG, - // Space - SP, - // Symbols allowing break after - SY, - // Word joiner - WJ, - // Unknown - XX, - // Zero width space - ZW, - // Zero width joiner - ZWJ, -}; - -// East-asian width. See UAX #11. -export type ea = enum { - // Ambiguous - A, - // Fullwidth - F, - // Halfwidth - H, - // Neutral - N, - // Narrow - NA, - // Wide - W, -}; - -// Case property. See Unicode section 4.2. -export type case = enum uint { - UPPER = 1 << 0, - LOWER = 1 << 1, - OTHER_UPPER = 1 << 2, - OTHER_LOWER = 1 << 3, -}; - -// Casing attributes. See Unicode section 4.2. -export type case_attrs = enum uint { - // Case ignorable - CI = 1 << 0, - // Cased - CASED = 1 << 1, - // Changes when casefolded - CWCF = 1 << 2, - // Changes when casemapped - CWCM = 1 << 3, - // Changes when lowercased - CWL = 1 << 4, - // Changes when NFKC casefolded - CWKCF = 1 << 5, - // Changes when titlecased - CWT = 1 << 6, - // Changes when uppercased - CWU = 1 << 7, - // NFKC casefold - NFKC_CF = 1 << 8, -}; - -// Script property. See UAX #24. -export type script = enum { - ADLM, - AGHB, - AHOM, - ARAB, - ARMI, - ARMN, - AVST, - BALI, - BAMU, - BASS, - BATK, - BENG, - BHKS, - BOPO, - BRAH, - BRAI, - BUGI, - BUHD, - CAKM, - CANS, - CARI, - CHAM, - CHER, - CHRS, - COPT, - CPRT, - CYRL, - DEVA, - DIAK, - DOGR, - DSRT, - DUPL, - ELBA, - ELYM, - EGYP, - ETHI, - GEOR, - GLAG, - GONG, - GONM, - GOTH, - GRAN, - GREK, - GUJR, - GURU, - HANG, - HANI, - HANO, - HATR, - HEBR, - HIRA, - HLUW, - HMNG, - HMNP, - HRKT, - HUNG, - ITAL, - JAVA, - KALI, - KANA, - KHAR, - KHMR, - KHOJ, - KITS, - KNDA, - KTHI, - LANA, - LAOO, - LATN, - LEPC, - LIMB, - LINA, - LINB, - LISU, - LYCI, - LYDI, - MAHJ, - MAKA, - MAND, - MANI, - MARC, - MEDF, - MEND, - MERC, - MERO, - MLYM, - MODI, - MONG, - MROO, - MTEI, - MULT, - MYMR, - NAND, - NARB, - NBAT, - NEWA, - NKOO, - NSHU, - OGAM, - OLCK, - ORKH, - ORYA, - OSGE, - OSMA, - PALM, - PAUC, - PERM, - PHAG, - PHLI, - PHLP, - PHNX, - PLRD, - PRTI, - QAAI, - ROHG, - RJNG, - RUNR, - SAMR, - SARB, - SAUR, - SGNW, - SHAW, - SHRD, - SIDD, - SIND, - SINH, - SOGD, - SOGO, - SORA, - SOYO, - SUND, - SYLO, - SYRC, - TAGB, - TAKR, - TALE, - TALU, - TAML, - TANG, - TAVT, - TELU, - TFNG, - TGLG, - THAA, - THAI, - TIBT, - TIRH, - UGAR, - VAII, - WARA, - WCHO, - XPEO, - XSUX, - YEZI, - YIII, - ZANB, - ZINH, - ZYYY, - ZZZZ, -}; - -// Hangul syllable type. See Unicode section 3.12 and 18.6. -export type hst = enum { - // Leading consonant - L, - // LV syllable - LV, - // LVT syllable - LVT, - // Trailing consonant - T, - // Vowel - V, - // Non-applicable - NA, -}; - -// Indic syllabic category. See IndicSyllabicCategory.txt in the UCD. -export type insc = enum { - AVAGRAHA, - BINDU, - BRAHMI_JOINING_NUMBER, - CANTILLATION_MARK, - CONSONANT, - CONSONANT_DEAD, - CONSONANT_FINAL, - CONSONANT_HEAD_LETTER, - CONSONANT_INITIAL_POSTFIXED, - CONSONANT_KILLER, - CONSONANT_MEDIAL, - CONSONANT_PLACEHOLDER, - CONSONANT_PRECEDING_REPHA, - CONSONANT_PREFIXED, - CONSONANT_REPHA, - CONSONANT_SUBJOINED, - CONSONANT_SUCCEEDING_REPHA, - CONSONANT_WITH_STACKER, - GEMINATION_MARK, - INVISIBLE_STACKER, - JOINER, - MODIFYING_LETTER, - NON_JOINER, - NUKTA, - NUMBER, - NUMBER_JOINER, - OTHER, - PURE_KILLER, - REGISTER_SHIFTER, - SYLLABLE_MODIFIER, - TONE_LETTER, - TONE_MARK, - VIRAMA, - VISARGA, - VOWEL, - VOWEL_DEPENDENT, - VOWEL_INDEPENDENT, -}; - -// Indic positional category. See IndicPositionalCategory.txt in the UCD. -export type inpc = enum { - BOTTOM, - BOTTOM_AND_LEFT, - BOTTOM_AND_RIGHT, - LEFT, - LEFT_AND_RIGHT, - NA, - OVERSTRUCK, - RIGHT, - TOP, - TOP_AND_BOTTOM, - TOP_AND_BOTTOM_AND_LEFT, - TOP_AND_BOTTOM_AND_RIGHT, - TOP_AND_LEFT, - TOP_AND_LEFT_AND_RIGHT, - TOP_AND_RIGHT, - VISUAL_ORDER_LEFT, -}; - -// Identifier and pattern properties. See UAX #31. -export type id = enum uint { - IDS = 1 << 0, - IDC = 1 << 1, - OIDS = 1 << 2, - OIDC = 1 << 2, - XIDS = 1 << 3, - XIDC = 1 << 4, - SYN = 1 << 5, - WS = 1 << 6, -}; - -// Properties related to function and graphics characteristics. This is a -// synethetic type based on mulitple Unicode properties listed in UAX #42 -// section 4.4.10. -export type fgc = enum uint { - DASH = 1 << 0, - HYPHEN = 1 << 1, - QUOTATION_MARK = 1 << 2, - TERMINAL_PUNCTUATION = 1 << 3, - SENTENCE_TERMINAL = 1 << 4, - DIACRITIC = 1 << 5, - EXTENDER = 1 << 6, - SOFT_DOTTED = 1 << 7, - ALPHABETIC = 1 << 8, - OTHER_ALPHABETIC = 1 << 9, - MATH = 1 << 10, - OTHER_MATH = 1 << 11, - HEX_DIGIT = 1 << 12, - ASCII_HEX_DIGIT = 1 << 13, - DEFAULT_IGNORABLE_CODE_POINT = 1 << 14, - OTHER_DEFAULT_IGNORABLE_CODE_POINT = 1 << 15, - LOGICAL_ORDER_EXCEPTION = 1 << 16, - PREPENDED_CONCATENATION_MARK = 1 << 17, - WHITE_SPACE = 1 << 18, - VERTICAL_ORIENTATION = 1 << 19, - REGIONAL_INDICATOR = 1 << 20, -}; - -// Properties related to boundaries. This is a synethetic type based on mulitple -// Unicode properties listed in UAX #42 section 4.4.20. -export type gr = enum uint { - GR_BASE = 1 << 0, - GR_EXT = 1 << 1, -}; - -// Grapheme cluster break. See UAX #29. -export type gcb = enum { - XX, - CN, - CR, - EX, - L, - LF, - LV, - LVT, - PP, - RI, - SM, - T, - V, - ZWJ, -}; - -// Word break. See UAX #29. -export type wb = enum { - XX, - CR, - DQ, - EX, - EXTEND, - FO, - HL, - KA, - LE, - LF, - MB, - ML, - MN, - NL, - NU, - RI, - SQ, - WSEGSPACE, - ZWJ, -}; - -// Sentence break. See UAX #29. -export type sb = enum { - XX, - AT, - CL, - CR, - EX, - FO, - LE, - LF, - LO, - NU, - SC, - SE, - SP, - ST, - UP, -}; - -// Properties related to ideographs. This is a synethetic type based on mulitple -// Unicode properties listed in UAX #42 section 4.4.21. -export type ideo = enum uint { - IDEO = 1 << 1, - UIDEO = 1 << 2, - IDSB = 1 << 3, - IDST = 1 << 4, - RADICAL = 1 << 5, -}; - -// Miscellaneous properties. This is a synethetic type based on mulitple Unicode -// properties listed in UAX #42 section 4.4.22. -export type misc = enum uint { - DEP = 1 << 0, - VS = 1 << 1, - NCHAR = 1 << 2, -}; - -// Properties related to Emoji. This is a synethetic type based on mulitple -// Unicode properties listed in UAX #42 section 4.4.26. -export type emoji = enum uint { - EMOJI = 1 << 0, - EPRES = 1 << 1, - EMOD = 1 << 2, - EBASE = 1 << 3, - ECOMP = 1 << 4, - EXTPICT = 1 << 5, -}; diff --git a/unix/+linux/nice.ha b/unix/+linux/nice.ha @@ -16,8 +16,9 @@ export fn nice(inc: int) (void | errors::error) = { if (prio < -20) { prio = -20; }; - return match (rt::setpriority(rt::PRIO_PROCESS, 0, prio)) { - void => void, - err: rt::errno => errors::errno(err), + match (rt::setpriority(rt::PRIO_PROCESS, 0, prio)) { + case void => void; + case err: rt::errno => + return errors::errno(err); }; }; diff --git a/unix/+linux/umask.ha b/unix/+linux/umask.ha @@ -5,8 +5,10 @@ use rt; // Sets the file mode creation mask for the current process and return the // previous value of the mask. export fn umask(mode: fs::mode) (fs::mode | errors::error) = { - return match (rt::umask(mode)) { - mode: rt::mode_t => mode: fs::mode, - err: rt::errno => errors::errno(err), + match (rt::umask(mode)) { + case mode: rt::mode_t => + return mode: fs::mode; + case err: rt::errno => + return errors::errno(err); }; }; diff --git a/unix/hosts/lookup.ha b/unix/hosts/lookup.ha @@ -18,8 +18,10 @@ export fn lookup(name: str) []ip::addr = { let addrs: []ip::addr = []; for (true) { const line = match (bufio::scanline(&file)) { - io::EOF => break, - line: []u8 => line, + case io::EOF => + break; + case line: []u8 => + yield line; }; defer free(line); if (len(line) == 0 || line[0] == '#': u32: u8) { @@ -30,16 +32,20 @@ export fn lookup(name: str) []ip::addr = { defer io::close(scanner); const tok = match (bufio::scantok(scanner, ' ', '\t')!) { - io::EOF => break, - tok: []u8 => tok, + case io::EOF => + break; + case tok: []u8 => + yield tok; }; defer free(tok); const addr = ip::parse(strings::fromutf8(tok))!; for (true) { const tok = match (bufio::scantok(scanner, ' ', '\t')!) { - io::EOF => break, - tok: []u8 => tok, + case io::EOF => + break; + case tok: []u8 => + yield tok; }; defer free(tok); diff --git a/unix/passwd/group.ha b/unix/passwd/group.ha @@ -20,12 +20,16 @@ export type grent = struct { // using [[grent_finish]]. export fn nextgr(stream: *io::stream) (grent | io::EOF | io::error | invalid) = { let line = match (bufio::scanline(stream)?) { - ln: []u8 => ln, - io::EOF => return io::EOF, + case ln: []u8 => + yield ln; + case io::EOF => + return io::EOF; }; let line = match (strings::try_fromutf8(line)) { - s: str => s, - * => return invalid, + case s: str => + yield s; + case => + return invalid; }; let fields = strings::split(line, ":"); @@ -36,8 +40,10 @@ export fn nextgr(stream: *io::stream) (grent | io::EOF | io::error | invalid) = }; let gid = match (strconv::stou(fields[2])) { - u: uint => u, - * => return invalid, + case u: uint => + yield u; + case => + return invalid; }; return grent { @@ -61,16 +67,21 @@ export fn grent_finish(ent: grent) void = { // See [[nextgr]] for low-level parsing API. export fn getgroup(name: str) (grent | void) = { let file = match (os::open("/etc/group")) { - f: io::file => f, - * => abort("Unable to open /etc/group"), + case f: io::file => + yield f; + case => + abort("Unable to open /etc/group"); }; defer io::close(&file); for (true) { let ent = match (nextgr(&file)) { - e: grent => e, - io::EOF => break, - * => abort("Invalid entry in /etc/group"), + case e: grent => + yield e; + case io::EOF => + break; + case => + abort("Invalid entry in /etc/group"); }; if (ent.name == name) { diff --git a/unix/passwd/passwd.ha b/unix/passwd/passwd.ha @@ -26,12 +26,16 @@ export type pwent = struct { // result using [[pwent_finish]]. export fn nextpw(stream: *io::stream) (pwent | io::EOF | io::error | invalid) = { let line = match (bufio::scanline(stream)?) { - io::EOF => return io::EOF, - ln: []u8 => ln, + case io::EOF => + return io::EOF; + case ln: []u8 => + yield ln; }; let line = match (strings::try_fromutf8(line)) { - s: str => s, - * => return invalid, + case s: str => + yield s; + case => + return invalid; }; let fields = strings::split(line, ":"); @@ -42,13 +46,17 @@ export fn nextpw(stream: *io::stream) (pwent | io::EOF | io::error | invalid) = }; let uid = match (strconv::stou(fields[2])) { - u: uint => u, - * => return invalid, + case u: uint => + yield u; + case => + return invalid; }; let gid = match (strconv::stou(fields[3])) { - u: uint => u, - * => return invalid, + case u: uint => + yield u; + case => + return invalid; }; return pwent { @@ -78,16 +86,21 @@ export fn pwent_finish(ent: pwent) void = { // See [[nextpw]] for low-level parsing API. export fn getuser(username: str) (pwent | void) = { let file = match (os::open("/etc/passwd")) { - f: io::file => f, - * => abort("Can't open /etc/passwd"), + case f: io::file => + yield f; + case => + abort("Can't open /etc/passwd"); }; defer io::close(&file); for (true) { let ent = match (nextpw(&file)) { - e: pwent => e, - io::EOF => break, - * => abort("Invalid entry in /etc/passwd"), + case e: pwent => + yield e; + case io::EOF => + break; + case => + abort("Invalid entry in /etc/passwd"); }; if (ent.username == username) { diff --git a/unix/poll/+linux.ha b/unix/poll/+linux.ha @@ -38,8 +38,10 @@ export fn poll( ) (uint | errors::error) = { let ts = rt::timespec { ... }; time::duration_to_timespec(timeout, &ts); - return match (rt::ppoll(fds: *[*]pollfd, len(fds), &ts, null)) { - err: rt::errno => errors::errno(err), - n: int => n: uint, + match (rt::ppoll(fds: *[*]pollfd, len(fds), &ts, null)) { + case err: rt::errno => + return errors::errno(err); + case n: int => + return n: uint; }; }; diff --git a/unix/resolvconf/load.ha b/unix/resolvconf/load.ha @@ -27,8 +27,10 @@ export fn load() []ip::addr = { for (true) { const line = match (bufio::scanline(&file)) { - io::EOF => break, - line: []u8 => line, + case io::EOF => + break; + case line: []u8 => + yield line; }; defer free(line); if (len(line) == 0 || line[0] == '#': u32: u8) { @@ -39,8 +41,10 @@ export fn load() []ip::addr = { defer io::close(scanner); const tok = match (bufio::scantok(scanner, ' ', '\t')!) { - io::EOF => break, - tok: []u8 => tok, + case io::EOF => + break; + case tok: []u8 => + yield tok; }; defer free(tok); if (strings::fromutf8(tok) != "nameserver") { @@ -48,8 +52,10 @@ export fn load() []ip::addr = { }; const tok = match (bufio::scantok(scanner, ' ')!) { - io::EOF => break, - tok: []u8 => tok, + case io::EOF => + break; + case tok: []u8 => + yield tok; }; defer free(tok); append(cache, ip::parse(strings::fromutf8(tok))!); diff --git a/unix/tty/+linux/isatty.ha b/unix/tty/+linux/isatty.ha @@ -5,12 +5,16 @@ use os; // Returns whether the given stream is connected to a terminal. export fn isatty(stream: *io::stream) bool = { let fd = match (io::unwrapfd(stream)) { - void => return false, - fd: int => fd, + case void => + return false; + case fd: int => + yield fd; }; let wsz = rt::winsize { ... }; - return match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *void)) { - e: rt::errno => false, - r: int => if (r == 0) true else false, + match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *void)) { + case e: rt::errno => + return false; + case r: int => + return r == 0; }; }; diff --git a/unix/tty/+linux/open.ha b/unix/tty/+linux/open.ha @@ -6,9 +6,10 @@ use os; // Returns a stream connected to the TTY of the current process. The caller must // close it using [[io::close]]. export fn open() (io::file | error) = { - return match (os::open("/dev/tty", fs::flags::RDWR, - fs::flags::CLOEXEC)) { - f: io::file => f, - fs::error => errors::noentry, + match (os::open("/dev/tty", fs::flags::RDWR, fs::flags::CLOEXEC)) { + case f: io::file => + return f; + case fs::error => + return errors::noentry; }; }; diff --git a/unix/tty/+linux/winsize.ha b/unix/tty/+linux/winsize.ha @@ -6,19 +6,26 @@ use rt; // Returns the dimensions of underlying terminal of the stream. export fn winsize(tty: *io::stream) (ttysize | error) = { let fd = match (io::unwrapfd(tty)) { - void => return errors::unsupported, - fd: int => fd, + case void => + return errors::unsupported; + case fd: int => + yield fd; }; let wsz = rt::winsize { ... }; - return match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *void)) { - e: rt::errno => switch (e: int) { - rt::EBADFD => errors::invalid, - rt::ENOTTY => errors::unsupported, - * => abort("unreachable"), - }, - int => ttysize { + match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *void)) { + case e: rt::errno => + switch (e: int) { + case rt::EBADFD => + return errors::invalid; + case rt::ENOTTY => + return errors::unsupported; + case => + abort("Unexpected error from ioctl"); + }; + case int => + return ttysize { rows = wsz.ws_row, columns = wsz.ws_col, - }, + }; }; }; diff --git a/uuid/uuid.ha b/uuid/uuid.ha @@ -111,23 +111,30 @@ export fn decode(in: *io::stream) (uuid | invalid | io::error) = { for (let i = 0z; i < len(u); i += 1) { let buf: [2]u8 = [0...]; match (io::read(in, buf)?) { - io::EOF => return invalid, - z: size => assert(z == len(buf)), + case io::EOF => + return invalid; + case z: size => + assert(z == len(buf)); }; u[i] = match (strconv::stou8b( strings::fromutf8_unsafe(buf), strconv::base::HEX)) { - strconv::overflow => abort(), - strconv::invalid => return invalid, - u: u8 => u, + case strconv::overflow => + abort(); + case strconv::invalid => + return invalid; + case u: u8 => + yield u; }; if (i + 1 == TIME_MID || i + 1 == TIME_HI_AND_VERSION || i + 1 == CLOCK_SEQ_HI_AND_RESERVED || i + 1 == NODE) { match (io::read(in, buf[..1])?) { - io::EOF => return invalid, - z: size => assert(z == 1), + case io::EOF => + return invalid; + case z: size => + assert(z == 1); }; if (buf[0] != '-': u32: u8) { return invalid; @@ -141,18 +148,23 @@ export fn decode(in: *io::stream) (uuid | invalid | io::error) = { export fn decodestr(in: str) (uuid | invalid) = { let buf = bufio::fixed(strings::toutf8(in), io::mode::READ); defer io::close(buf); - return match (decode(buf)) { - err: io::error => abort(), - invalid => invalid, - u: uuid => u, + match (decode(buf)) { + case err: io::error => + abort(); + case invalid => + return invalid; + case u: uuid => + return u; }; }; @test fn decode() void = { let in = "3ded910c-8080-4bc8-af39-b6cccee36741"; let id = match (decodestr(in)) { - invalid => abort(), - u: uuid => u, + case invalid => + abort(); + case u: uuid => + yield u; }; assert(compare(id, [ 0x3d, 0xed, 0x91, 0x0c, 0x80, 0x80, 0x4b, 0xc8,