hare

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

commit d2bd3e64b09c393b8779475e329b8d7ca3fdeae5
parent 45235f53fe8121615aad186108880d91c8877d8e
Author: Drew DeVault <sir@cmpwn.com>
Date:   Tue, 23 Feb 2021 15:00:59 -0500

all: use error propagation where appropriate

Diffstat:
Mencoding/utf8/decode.ha | 6+++---
Mfmt/fmt.ha | 48++++++++++++++++--------------------------------
Mhare/lex/lex.ha | 60++++++++++++++++++++----------------------------------------
Mio/copy.ha | 13++++---------
Mio/types.ha | 8++++----
Mrt/+linux/errno.ha | 2+-
Mrt/+linux/stat.ha | 15++++++---------
Mrt/+linux/syscalls.ha | 92+++++++++++++++++++++++++------------------------------------------------------
Mstrconv/stoi.ha | 58+++++++++++++++++++---------------------------------------
Mstrconv/stou.ha | 46+++++++++++++++-------------------------------
Mstrconv/types.ha | 4++--
11 files changed, 119 insertions(+), 233 deletions(-)

diff --git a/encoding/utf8/decode.ha b/encoding/utf8/decode.ha @@ -19,7 +19,7 @@ export fn decode(src: (str | []u8)) decoder = match (src) { export type more = void; // An error indicating that an invalid UTF-8 sequence was found. -export type invalid = void; +export type invalid = void!; // Returns the next rune from a decoder. If the slice ends with a complete UTF-8 // sequence, void is returned. If an incomplete sequence is encountered, more is @@ -96,7 +96,7 @@ 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(), + (invalid | more | void) => abort(), r: rune => assert(r == expected[i]), }; }; @@ -104,7 +104,7 @@ export fn prev(d: *decoder) (rune | void | more | invalid) = { assert(decoder.offs == len(decoder.src)); for (let i = 0z; i < len(expected); i += 1) { match (prev(&decoder)) { - (invalid | more | void ) => abort(), + (invalid | more | void) => abort(), r: rune => assert(r == expected[len(expected) - i - 1]), }; }; diff --git a/fmt/fmt.ha b/fmt/fmt.ha @@ -82,6 +82,16 @@ export @noreturn fn fatal(fmt: str, args: formattable...) void = { os::exit(1); }; +// Formats text for printing and writes it to an [io::stream], followed by a +// line feed. +export fn fprintln( + s: *io::stream, + fmt: str, + args: formattable... +) (io::error | size) = { + return fprintf(s, fmt, args...)? + io::write(s, ['\n': u32: u8])?; +}; + type negation = enum { NONE, SPACE, @@ -130,13 +140,9 @@ export fn fprintf( r: rune => r, }; - const arg = if (r == '{') match (io::write( - s, utf8::encode_rune('{'))) { - err: io::error => return err, - w: size => { - n += w; - continue; - }, + const arg = if (r == '{') { + n += io::write(s, utf8::encode_rune('{'))?; + continue; } else if (ascii::isdigit(r)) { strings::push(&iter, r); args[scan_uint(&iter)]; @@ -164,37 +170,15 @@ export fn fprintf( r: rune => assert(r == '}', "Invalid format string (hanging '}')"), }; - match (io::write(s, utf8::encode_rune('}'))) { - err: io::error => return err, - w: size => n += w, - }; - } else match (io::write(s, utf8::encode_rune(r))) { - err: io::error => return err, - w: size => n += w, + n += io::write(s, utf8::encode_rune('}'))?; + } else { + n += io::write(s, utf8::encode_rune(r))?; }; }; return n; }; -// Formats text for printing and writes it to an [io::stream], followed by a -// line feed. -export fn fprintln( - s: *io::stream, - fmt: str, - args: formattable... -) (io::error | size) = { - let z = match(fprintf(s, fmt, args...)) { - err: io::error => return err, - z: size => z, - }; - z += match (io::write(s, ['\n': u32: u8])) { - err: io::error => return err, - z: size => z, - }; - return z; -}; - fn format( out: *io::stream, arg: formattable, diff --git a/hare/lex/lex.ha b/hare/lex/lex.ha @@ -16,10 +16,10 @@ export type lexer = struct { }; // A syntax error -export type syntax = (location, str); +export type syntax = (location, str)!; // All possible lexer errors -export type error = (io::error | syntax); +export type error = (io::error | syntax)!; export fn errstr(err: error) const str = { return match (err) { @@ -48,8 +48,7 @@ export fn lex(lex: *lexer) ((token, location) | io::EOF | error) = { }; let loc = location { ... }; - let r: rune = match (nextw(lex)) { - e: io::error => return e, + let r: rune = match (nextw(lex)?) { io::EOF => return io::EOF, r: (rune, location) => { loc = r.1; @@ -104,10 +103,9 @@ fn lex_unicode(lex: *lexer, loc: location, n: size) (rune | error) = { assert(n < 9); let buf: [9]u8 = [0...]; for (let i = 0z; i < n; i += 1z) { - let r = match (next(lex)) { + let r = match (next(lex)?) { io::EOF => return syntaxerr(loc, "unexpected EOF scanning for escape"), - err: io::error => return err, r: rune => r, }; if (!ascii::isxdigit(r)) { @@ -124,19 +122,17 @@ 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)) { + let r = match (next(lex)?) { io::EOF => return syntaxerr(loc, "unexpected EOF scanning for rune"), - err: io::error => return err, r: rune => r, }; if (r != '\\') { return r; }; - r = match (next(lex)) { + r = match (next(lex)?) { io::EOF => return syntaxerr(loc, "unexpected EOF scanning for escape"), - err: io::error => return err, r: rune => r, }; return switch (r) { @@ -162,17 +158,13 @@ fn lex_string( loc: location, ) ((token, location) | io::EOF | error) = { let chars: []u8 = []; - for (true) match (next(lex)) { - err: io::error => return err, + for (true) match (next(lex)?) { io::EOF => return syntaxerr(loc, "unexpected EOF scanning string literal"), r: rune => if (r == '"') break else { unget(lex, r); - r = match (lex_rune(lex, loc)) { - err: error => return err, - r: rune => r, - }; + r = lex_rune(lex, loc)?; append(chars, ...utf8::encode_rune(r)); }, }; @@ -197,16 +189,11 @@ fn lex_rn_str( }; // Rune literal - let ret = match (lex_rune(lex, loc)) { - err: error => return err, - r: rune => (literal { - storage = literal_type::RUNE, - _rune = r, - }: token, loc), - }; - match (next(lex)) { - err: io::error => - return err, + let ret = (literal { + storage = literal_type::RUNE, + _rune = lex_rune(lex, loc)?, + }: token, loc); + match (next(lex)?) { io::EOF => return syntaxerr(loc, "unexpected EOF"), n: rune => if (n != '\'') @@ -228,9 +215,8 @@ fn lex_name( (io::EOF | io::error) => abort(), }; - for (true) match (next(lex)) { + for (true) match (next(lex)?) { io::EOF => break, - err: io::error => return err, r: rune => { if (!is_name(r, true)) { unget(lex, r); @@ -258,8 +244,7 @@ fn lex2( loc: location, r: rune, ) ((token, location) | io::EOF | error) = { - let n = match (next(lexr)) { - err: io::error => return err, + let n = match (next(lexr)?) { io::EOF => io::EOF, r: rune => r, }; @@ -284,8 +269,7 @@ fn lex2( '=' => return (btoken::DIVEQ: token, loc), '/' => { // Comment - for (true) match (next(lexr)) { - err: io::error => return err, + for (true) match (next(lexr)?) { io::EOF => break, r: rune => if (r == '\n') { break; @@ -359,8 +343,7 @@ fn lex3( loc: location, r: rune, ) ((token, location) | io::EOF | error) = { - let n = match (next(lex)) { - err: io::error => return err, + let n = match (next(lex)?) { io::EOF => return switch (r) { '.' => (btoken::DOT: token, loc), '<' => (btoken::LESS: token, loc), @@ -383,8 +366,7 @@ fn lex3dot( ) ((token, location) | io::EOF | error) = { let tok: token = switch (n) { '.' => { - let q = match (next(lex)) { - err: io::error => return err, + let q = match (next(lex)?) { io::EOF => io::EOF, r: rune => r, }; @@ -413,8 +395,7 @@ fn lex3lt( ) ((token, location) | io::EOF | error) = { let tok: token = switch (n) { '<' => { - let q = match (next(lex)) { - err: io::error => return err, + let q = match (next(lex)?) { io::EOF => io::EOF, r: rune => r, }; @@ -444,8 +425,7 @@ fn lex3gt( ) ((token, location) | io::EOF | error) = { let tok: token = switch (n) { '>' => { - let q = match (next(lex)) { - err: io::error => return err, + let q = match (next(lex)?) { io::EOF => io::EOF, r: rune => r, }; diff --git a/io/copy.ha b/io/copy.ha @@ -15,16 +15,11 @@ export fn copy(dest: *stream, src: *stream) (error | size) = { let w = 0z; static let buf: [4096]u8 = [0...]; for (true) { - match (read(src, buf[..])) { - err: error => return err, + match (read(src, buf[..])?) { n: size => for (let i = 0z; i < n) { - match (write(dest, buf[i..n])) { - err: error => return err, - r: size => { - w += r; - i += r; - }, - }; + let r = write(dest, buf[i..n])?; + w += r; + i += r; }, EOF => break, }; diff --git a/io/types.ha b/io/types.ha @@ -2,16 +2,16 @@ export type os_error = struct { string: *fn(data: *void) str, data: *void, -}; +}!; // An error indicating that the underlying stream has been closed. -export type closed = void; +export type closed = void!; // An error indicating that the requested operation is not supported. -export type unsupported = void; +export type unsupported = void!; // Any error which may be returned from an I/O function. -export type error = (os_error | closed | unsupported); +export type error = (os_error | closed | unsupported)!; // Indicates an end-of-file condition. export type EOF = void; diff --git a/rt/+linux/errno.ha b/rt/+linux/errno.ha @@ -1,5 +1,5 @@ // Represents an error returned from the Linux kernel. -export type errno = int; +export type errno = int!; // Given an integer error number, wraps it in an error type. export fn wrap_errno(err: int) errno = err: errno; diff --git a/rt/+linux/stat.ha b/rt/+linux/stat.ha @@ -10,11 +10,11 @@ fn fstatat_statx( flags: int, mask: uint, statbuf: *stx, -) (void | errno) = match (wrap_return(syscall5( - SYS_statx, dirfd: u64, path: uintptr: u64, flags: u64, - mask: u64, statbuf: uintptr: u64))) { - err: errno => err, - u64 => void, +) (void | errno) = { + wrap_return(syscall5(SYS_statx, + dirfd: u64, path: uintptr: u64, flags: u64, + mask: u64, statbuf: uintptr: u64))?; + return; }; export fn fstatat( @@ -24,10 +24,7 @@ export fn fstatat( flags: int, ) (errno | void) = { let statxbuf = stx { ... }; - match (fstatat_statx(dirfd, path, flags, STATX_BASIC_STATS, &statxbuf)) { - err: errno => return err, - void => void, - }; + fstatat_statx(dirfd, path, flags, STATX_BASIC_STATS, &statxbuf)?; statbuf.dev = mkdev(statxbuf.dev_major, statxbuf.dev_minor); statbuf.ino = statxbuf.ino; statbuf.mode = statxbuf.mode; diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha @@ -7,34 +7,23 @@ fn syscall5(u64, u64, u64, u64, u64, u64) u64; fn syscall6(u64, u64, u64, u64, u64, u64, u64) u64; export fn read(fd: int, buf: *void, count: size) (size | errno) = { - return match (wrap_return(syscall3(SYS_read, - fd: u64, buf: uintptr: u64, count: u64))) { - err: errno => err, - n: u64 => n: size, - }; + return wrap_return(syscall3(SYS_read, + fd: u64, buf: uintptr: u64, count: u64))?: size; }; export fn write(fd: int, buf: *const void, count: size) (size | errno) = { - return match (wrap_return(syscall3(SYS_write, - fd: u64, buf: uintptr: u64, count: u64))) { - err: errno => err, - n: u64 => n: size, - }; + return wrap_return(syscall3(SYS_write, + fd: u64, buf: uintptr: u64, count: u64))?: size; }; export fn open(path: *const char, flags: int, mode: uint) (int | errno) = { - return match (wrap_return(syscall4(SYS_openat, AT_FDCWD: u64, - path: uintptr: u64, flags: u64, mode: u64))) { - err: errno => err, - n: u64 => n: int, - }; + return wrap_return(syscall4(SYS_openat, AT_FDCWD: u64, + path: uintptr: u64, flags: u64, mode: u64))?: int; }; export fn close(fd: int) (void | errno) = { - return match (wrap_return(syscall1(SYS_close, fd: u64))) { - err: errno => err, - u64 => void, - }; + wrap_return(syscall1(SYS_close, fd: u64))?; + return; }; export fn execveat(dirfd: int, path: *const char, argv: *[*]nullable *const char, @@ -58,27 +47,19 @@ export fn sendfile( in: int, offs: nullable *size, count: size, -) (size | errno) = match (wrap_return(syscall4(SYS_sendfile, - out: u64, in: u64, offs: uintptr: u64, count: u64))) { - n: u64 => n: size, - err: errno => err, -}; +) (size | errno) = wrap_return(syscall4(SYS_sendfile, + out: u64, in: u64, offs: uintptr: u64, count: u64))?: size; export @noreturn fn exit(status: int) void = syscall1(SYS_exit, status: u64); export fn kill(pid: int, signal: int) (void | errno) = { - return match (wrap_return(syscall2(SYS_kill, pid: u64, signal: u64))) { - err: errno => err, - u64 => void, - }; + wrap_return(syscall2(SYS_kill, pid: u64, signal: u64))?; + return; }; export fn pipe2(pipefd: *[2]int, flags: int) (void | errno) = { - return match (wrap_return(syscall2(SYS_pipe2, - pipefd: uintptr: u64, flags: u64))) { - err: errno => err, - u64 => void, - }; + wrap_return(syscall2(SYS_pipe2, pipefd: uintptr: u64, flags: u64))?; + return; }; export fn mmap( @@ -107,29 +88,22 @@ export fn mmap( }; export fn munmap(addr: *void, length: size) (void | errno) = { - return match (wrap_return(syscall2(SYS_munmap, - addr: uintptr: u64, length: u64))) { - err: errno => err, - u64 => void, - }; + wrap_return(syscall2(SYS_munmap, + addr: uintptr: u64, length: u64))?; + return; }; export fn mprotect(addr: *void, length: size, prot: uint) (void | errno) = { - return match (wrap_return(syscall3(SYS_mprotect, - addr: uintptr: u64, length: u64, prot: u64))) { - err: errno => err, - u64 => void, - }; + wrap_return(syscall3(SYS_mprotect, + addr: uintptr: u64, length: u64, prot: u64))?; + return; }; export fn lseek(fd: int, off: i64, whence: uint) (i64 | errno) = { - return match (wrap_return(syscall3(SYS_lseek, - fd: u64, off: u64, whence: u64))) { - err: errno => err, - n: u64 => n: i64, - }; + return wrap_return(syscall3(SYS_lseek, + fd: u64, off: u64, whence: u64))?: i64; }; export fn faccessat( @@ -158,31 +132,23 @@ 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 match (wrap_return(match (arg) { + 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), - })) { - err: errno => err, - n: u64 => n: int, - }; + })?: int; }; export fn getrandom(buf: *void, bufln: size, flags: uint) (size | errno) = { - return match (wrap_return(syscall3(SYS_getrandom, - buf: uintptr: u64, bufln: u64, flags: u64))) { - err: errno => err, - n: u64 => n: size, - }; + return wrap_return(syscall3(SYS_getrandom, + buf: uintptr: u64, bufln: u64, flags: u64))?: size; }; // TODO: Implement me with VDSO export fn clock_gettime(clock_id: int, tp: *timespec) (void | errno) = { - return match (wrap_return(syscall2(SYS_clock_gettime, - clock_id: u64, tp: uintptr: u64))) { - err: errno => err, - u64 => void, - }; + wrap_return(syscall2(SYS_clock_gettime, + clock_id: u64, tp: uintptr: u64))?; + return; }; diff --git a/strconv/stoi.ha b/strconv/stoi.ha @@ -16,15 +16,11 @@ export fn stoi64(s: str) (i64 | invalid | overflow) = { }; let u = if (sign < 0) stou64(strings::from_utf8_unsafe(b[1..])) else stou64(s); - match(u) { - v: (invalid | overflow) => return v, - n: u64 => { - if (n > max) { - return overflow; - }; - return n: i64 * sign; - }, + let n = u?; + if (n > max) { + return overflow; }; + return n: i64 * sign; }; // Converts a string to an i32 in base 10. If the string contains any @@ -32,15 +28,11 @@ export fn stoi64(s: str) (i64 | invalid | overflow) = { // [strconv::invalid] is returned. If the number is too large to be represented // by an i32, [strconv::overflow] is returned. export fn stoi32(s: str) (i32 | invalid | overflow) = { - match (stoi64(s)) { - v: (invalid | overflow) => return v, - n: i64 => { - if (n >= types::I32_MIN: i64 && n <= types::I32_MAX: i64) { - return n: i32; - }; - return overflow; - }, + let n = stoi64(s)?; + if (n >= types::I32_MIN: i64 && n <= types::I32_MAX: i64) { + return n: i32; }; + return overflow; }; // Converts a string to an i16 in base 10. If the string contains any @@ -48,15 +40,11 @@ export fn stoi32(s: str) (i32 | invalid | overflow) = { // [strconv::invalid] is returned. If the number is too large to be represented // by an i16, [strconv::overflow] is returned. export fn stoi16(s: str) (i16 | invalid | overflow) = { - match (stoi64(s)) { - v: (invalid | overflow) => return v, - n: i64 => { - if (n >= types::I16_MIN: i64 && n <= types::I16_MAX: i64) { - return n: i16; - }; - return overflow; - }, + let n = stoi64(s)?; + if (n >= types::I16_MIN: i64 && n <= types::I16_MAX: i64) { + return n: i16; }; + return overflow; }; // Converts a string to an i8 in base 10. If the string contains any @@ -64,15 +52,11 @@ export fn stoi16(s: str) (i16 | invalid | overflow) = { // [strconv::invalid] is returned. If the number is too large to be represented // by an i8, [strconv::overflow] is returned. export fn stoi8(s: str) (i8 | invalid | overflow) = { - match (stoi64(s)) { - v: (invalid | overflow) => return v, - n: i64 => { - if (n >= types::I8_MIN: i64 && n <= types::I8_MAX: i64) { - return n: i8; - }; - return overflow; - }, + let n= stoi64(s)?; + if (n >= types::I8_MIN: i64 && n <= types::I8_MAX: i64) { + return n: i8; }; + return overflow; }; // Converts a string to an int in base 10. If the string contains any @@ -81,11 +65,7 @@ export fn stoi8(s: str) (i8 | invalid | overflow) = { // by an int, [strconv::overflow] is returned. export fn stoi(s: str) (int | invalid | overflow) = { static assert(size(int) == size(i32) || size(int) == size(i64)); - return if (size(int) == size(i32)) match (stoi32(s)) { - v: (invalid | overflow) => v, - n: i32 => n: int, - } else match (stoi64(s)) { - v: (invalid | overflow) => v, - n: i64 => n: int, - }; + return + if (size(int) == size(i32)) stoi32(s)?: int + else stoi64(s)?: int; }; diff --git a/strconv/stou.ha b/strconv/stou.ha @@ -55,15 +55,11 @@ export fn stou64b(s: str, base: uint) (u64 | invalid | overflow) = { // the number is too large to be represented by a u32, [strconv::overflow] is // returned. Supported bases are 2, 8, 10 and 16. export fn stou32b(s: str, base: uint) (u32 | invalid | overflow) = { - match (stou64b(s, base)) { - v: (invalid | overflow) => return v, - n: u64 => { - if (n <= types::U32_MAX: u64) { - return n: u32; - }; - return overflow; - }, + let n = stou64b(s, base)?; + if (n <= types::U32_MAX: u64) { + return n: u32; }; + return overflow; }; // Converts a string to a u16 in the given base, If the string contains any @@ -71,15 +67,11 @@ export fn stou32b(s: str, base: uint) (u32 | invalid | overflow) = { // the number is too large to be represented by a u16, [strconv::overflow] is // returned. Supported bases are 2, 8, 10 and 16. export fn stou16b(s: str, base: uint) (u16 | invalid | overflow) = { - match (stou64b(s, base)) { - v: (invalid | overflow) => return v, - n: u64 => { - if (n <= types::U16_MAX: u64) { - return n: u16; - }; - return overflow; - }, + let n = stou64b(s, base)?; + if (n <= types::U16_MAX: u64) { + return n: u16; }; + return overflow; }; // Converts a string to a u8 in the given base, If the string contains any @@ -87,15 +79,11 @@ export fn stou16b(s: str, base: uint) (u16 | invalid | overflow) = { // the number is too large to be represented by a u8, [strconv::overflow] is // returned. Supported bases are 2, 8, 10 and 16. export fn stou8b(s: str, base: uint) (u8 | invalid | overflow) = { - match (stou64b(s, base)) { - v: (invalid | overflow) => return v, - n: u64 => { - if (n <= types::U8_MAX: u64) { - return n: u8; - }; - return overflow; - }, + let n = stou64b(s, base)?; + if (n <= types::U8_MAX: u64) { + return n: u8; }; + return overflow; }; // Converts a string to a uint in the given base, If the string contains any @@ -104,13 +92,9 @@ export fn stou8b(s: str, base: uint) (u8 | invalid | overflow) = { // returned. Supported bases are 2, 8, 10 and 16. export fn stoub(s: str, base: uint) (uint | invalid | overflow) = { static assert(size(uint) == size(u32) || size(uint) == size(u64)); - return if (size(uint) == size(u32)) match (stou32b(s, base)) { - v: (invalid | overflow) => v, - n: u32 => n: uint, - } else match (stou64b(s, base)) { - v: (invalid | overflow) => v, - n: u64 => n: uint, - }; + return + if (size(uint) == size(u32)) stou32b(s, base)?: uint + else stou64b(s, base)?: uint; }; // Converts a string to a size in the given base, If the string contains any diff --git a/strconv/types.ha b/strconv/types.ha @@ -1,9 +1,9 @@ // Indicates that the input string is not an integer -export type invalid = void; +export type invalid = void!; // Indicates that the input number is too large to be represented by the // requested data type -export type overflow = void; +export type overflow = void!; // The valid numeric bases for numeric conversions. export type base = enum uint {