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:
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
};