commit 82f8c418578bf8004975bf7ddfd218ba20f08cad
parent e4b13d5c8f052980a6457e044fdfbeffafb00525
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 19 Apr 2021 10:04:06 -0400
cmd/haredoc: initial commit
This is only at a proof-of-concept level of sophistication.
Diffstat:
3 files changed, 140 insertions(+), 0 deletions(-)
diff --git a/cmd/haredoc/errors.ha b/cmd/haredoc/errors.ha
@@ -0,0 +1,13 @@
+use hare::lex;
+use hare::parse;
+
+type eof = void;
+type syntaxerr = void;
+type error = (lex::error | parse::error | syntaxerr | eof)!;
+
+fn strerror(err: error) str = match (err) {
+ err: lex::error => lex::strerror(err),
+ err: parse::error => parse::strerror(err),
+ eof => "Unexpected EOF",
+ syntaxerr => "Syntax error",
+};
diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha
@@ -0,0 +1,37 @@
+use fmt;
+use hare::ast;
+use hare::lex;
+use hare::parse;
+use io;
+use os;
+use strings;
+
+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)),
+ };
+ for (let i = 0z; i < len(unit.decls); i += 1) {
+ const decl = unit.decls[i];
+ if (!decl.exported || decl.docs == "") {
+ continue;
+ };
+
+ 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,
+ };
+
+ match (unparse_decl(os::stdout, decl)) {
+ err: io::error => fmt::fatal(io::strerror(err)),
+ size => void,
+ };
+
+ fmt::println();
+ fmt::println();
+ };
+};
diff --git a/cmd/haredoc/unparse.ha b/cmd/haredoc/unparse.ha
@@ -0,0 +1,90 @@
+use io;
+use fmt;
+use hare::ast;
+use hare::lex;
+use hare::unparse;
+use strio;
+
+// Forked from [hare::unparse] to reformat the output in a more suitable form
+// for documentation.
+fn unparse_decl(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, ", ")?;
+ };
+ };
+ },
+ 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._type 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(out, 0, f.prototype._type as ast::func_type)?;
+ },
+ };
+ n += fmt::fprint(out, ";")?;
+ return n;
+};
+
+fn prototype(
+ out: *io::stream,
+ indent: size,
+ t: ast::func_type,
+) (size | io::error) = {
+ let n = 0z;
+ n += fmt::fprint(out, "(")?;
+ for (let i = 0z; i < len(t.params); i += 1) {
+ let param = t.params[i];
+ n += fmt::fprintf(out, "{}: ",
+ if (len(param.name) == 0) "_" else param.name)?;
+ n += unparse::_type(out, indent, *param._type)?;
+ if (i + 1 == len(t.params)
+ && t.variadism == ast::variadism::HARE) {
+ n += fmt::fprintf(out, "...")?;
+ };
+ if (i + 1 < len(t.params)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ if (t.variadism == ast::variadism::C) {
+ n += fmt::fprint(out, ", ...")?;
+ };
+ n += fmt::fprint(out, ") ")?;
+ n += unparse::_type(out, indent, *t.result)?;
+ return n;
+};