hare

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

commit 3f5cc750d181a1e173eec3046a7702a345413416
parent 2efbf1e82766551b2d185c7122ce6d0ff6dd68f4
Author: Drew DeVault <sir@cmpwn.com>
Date:   Mon, 19 Apr 2021 13:02:09 -0400

haredoc: flesh out command structure

Diffstat:
MMakefile | 3++-
Mcmd/haredoc/errors.ha | 4+++-
Acmd/haredoc/hare.ha | 26++++++++++++++++++++++++++
Mcmd/haredoc/main.ha | 81++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
4 files changed, 93 insertions(+), 21 deletions(-)

diff --git a/Makefile b/Makefile @@ -35,7 +35,8 @@ harec_srcs=\ haredoc_srcs=\ ./cmd/haredoc/main.ha \ ./cmd/haredoc/errors.ha \ - ./cmd/haredoc/unparse.ha + ./cmd/haredoc/unparse.ha \ + ./cmd/haredoc/hare.ha $(HARECACHE)/hare.ssa: $(hare_srcs) $(hare_stdlib_deps) @printf 'HAREC\t$@\n' diff --git a/cmd/haredoc/errors.ha b/cmd/haredoc/errors.ha @@ -1,13 +1,15 @@ use hare::lex; use hare::parse; +use io; type eof = void; type syntaxerr = void; -type error = (lex::error | parse::error | syntaxerr | eof)!; +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", }; diff --git a/cmd/haredoc/hare.ha b/cmd/haredoc/hare.ha @@ -0,0 +1,26 @@ +use fmt; +use hare::ast; +use os; +use strings; + +// Formats output as Hare source code (prototypes) +fn emit_hare(decls: []ast::decl) (void | error) = { + // TODO: Should we emit the dependencies, too? + for (let i = 0z; i < len(decls); i += 1) { + const decl = decls[i]; + if (!decl.exported || decl.docs == "") { + continue; + }; + + 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, + }; + + unparse_decl(os::stdout, decl)?; + fmt::print("\n\n")?; + }; +}; diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha @@ -1,4 +1,6 @@ use fmt; +use fs; +use getopt; use hare::ast; use hare::lex; use hare::parse; @@ -6,32 +8,73 @@ use io; use os; use strings; +type format = enum { + HARE, + TTY, + HTML, + GEMTEXT, +}; + export fn main() void = { - const lexer = lex::init(os::stdin, "<stdin>", lex::flags::COMMENTS); - const unit = match (parse::subunit(&lexer)) { - u: ast::subunit => u, - err: parse::error => fmt::fatal(parse::strerror(err)), + // TODO: Use format::TTY by default if stdout is a tty + let fmt = format::HARE; + const cmd = getopt::parse(os::args, + "reads and formats Hare documentation", + ('F', "format", "specify output format (hare, tty, html, or gemtext)"), + "[identifiers...]", + ); + defer getopt::finish(&cmd); + + 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), + * => abort(), + }; }; - for (let i = 0z; i < len(unit.decls); i += 1) { - const decl = unit.decls[i]; - if (!decl.exported || decl.docs == "") { - continue; + + let unit: []ast::subunit = []; + defer { + for (let i = 0z; i < len(unit); i += 1) { + ast::subunit_free(unit[i]); }; + free(unit); + }; - let 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 (let i = 0z; i < len(cmd.args); i += 1) { + match (scan(cmd.args[i])) { + u: ast::subunit => append(unit, u), + err: error => fmt::fatal("Error: {}", strerror(err)), }; + }; - match (unparse_decl(os::stdout, decl)) { - err: io::error => fmt::fatal(io::strerror(err)), - size => void, + for (let i = 0z; i < len(unit); i += 1) { + match (emit(unit[i].decls, fmt)) { + void => void, + err: error => fmt::fatal("Error: {}", strerror(err)), }; + }; +}; - fmt::println(); - fmt::println(); +fn scan(path: str) (ast::subunit | error) = { + const input = match (os::open(path)) { + s: *io::stream => s, + err: fs::error => fmt::fatal("Error reading {}: {}", + path, fs::strerror(err)), }; + defer io::close(input); + const lexer = lex::init(input, "<stdin>", lex::flags::COMMENTS); + return parse::subunit(&lexer)?; +}; + +fn emit(decls: []ast::decl, fmt: format) (void | error) = switch (fmt) { + format::HARE => emit_hare(decls), + format::TTY => abort(), // TODO + format::HTML => abort(), // TODO + format::GEMTEXT => abort(), // TODO };