hare

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

commit 6049e2c788b9f6d1784d02fbe1433b43c5722f94
parent b9b10630a680d773d657bf46a73a014915d2fb33
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 26 Mar 2021 12:47:46 -0400

cmd/harec: format errors with context

Diffstat:
Mbufio/scanner.ha | 9++++-----
Acmd/harec/errors.ha | 40++++++++++++++++++++++++++++++++++++++++
Mcmd/harec/main.ha | 12+++++++++---
3 files changed, 53 insertions(+), 8 deletions(-)

diff --git a/bufio/scanner.ha b/bufio/scanner.ha @@ -15,8 +15,7 @@ export fn scanbyte(stream: *io::stream) (u8 | io::EOF | io::error) = { }; // Reads a slice of bytes until the delimiter. Delimiter is not -// included. [io::EOF] is returned when stream emits EOF but nothing was -// read. Returned buffer has to be freed by the caller. +// included. The return value must be freed by the caller. export fn scantok(stream: *io::stream, delim: u8) ([]u8 | io::EOF | io::error) = { let buf: []u8 = []; @@ -41,9 +40,9 @@ export fn scantok(stream: *io::stream, delim: u8) ([]u8 | io::EOF | io::error) = }; // Reads a slice of bytes until a newline character (\n, 0x10). Newline itself -// is not included. [io::EOF] is returned when stream emits EOF but nothing was -// read. Returned buffer has to be freed by the caller. -export fn scanline(stream: *io::stream) ([]u8 | io::EOF | io::error) = scantok(stream, '\n': u32: u8); +// is not included. The return value must be freed by the caller. +export fn scanline(stream: *io::stream) ([]u8 | io::EOF | io::error) = + scantok(stream, '\n': u32: u8); // Reads a rune from a UTF-8 stream. export fn scanrune(stream: *io::stream) (rune | utf8::invalid | io::EOF | io::error) = { diff --git a/cmd/harec/errors.ha b/cmd/harec/errors.ha @@ -0,0 +1,40 @@ +use bufio; +use fmt; +use hare::lex; +use hare::parse; +use io; +use os; +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::errstr(err)), + }; +}; + +fn printerr_syntax(err: lex::syntax) void = { + let location = err.0, details = err.1; + let file = os::open(location.path) as *io::stream; + defer io::close(file); + + let line = 1u; + for (line < location.line) { + let r = bufio::scanrune(file) as rune; + if (r == '\n') { + line += 1u; + }; + }; + + let line = bufio::scanline(file) as []u8; + defer free(line); + let line = strings::fromutf8(line); + fmt::errorfln("{}:{},{}: Syntax error: {}", + location.path, location.line, location.col, details); + fmt::errorln(line); + for (let i = 0u; i < location.col - 2; i += 1) { + fmt::errorf(" "); + }; + fmt::errorln("^--- here"); +}; diff --git a/cmd/harec/main.ha b/cmd/harec/main.ha @@ -3,13 +3,19 @@ use hare::ast; use hare::lex; use hare::parse; use hare::unparse; +use io; use os; export fn main() void = { - let lexer = lex::init(os::stdin, "<stdin>"); + let input = os::open(os::args[1]) as *io::stream; + defer io::close(input); + + let lexer = lex::init(input, os::args[1]); let su = match (parse::subunit(&lexer)) { - e: parse::error => - fmt::fatal("Syntax error: {}", parse::errstr(e)), + err: parse::error => { + printerr(err); + os::exit(1); + }, u: ast::subunit => u, }; defer ast::subunit_free(su);