hare

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

commit 85c9efa37a8dd880f5eacbf3995bede80a81f797
parent c7ac5d879554e12bc5caa75f190c3c4bf9622f2b
Author: Sebastian <sebastian@sebsite.pw>
Date:   Sat, 30 Sep 2023 23:36:48 -0400

haredoc: refactor to use unparse::syn

Signed-off-by: Sebastian <sebastian@sebsite.pw>

Diffstat:
Mcmd/haredoc/doc/color.ha | 147++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mcmd/haredoc/doc/hare.ha | 83+------------------------------------------------------------------------------
Mcmd/haredoc/doc/html.ha | 519++++++++-----------------------------------------------------------------------
Mcmd/haredoc/doc/sort.ha | 42+++++++++++++++++++++++++++++++++---------
Mcmd/haredoc/doc/tty.ha | 535++++---------------------------------------------------------------------------
Mcmd/haredoc/doc/types.ha | 19++++++++++++++++++-
Mcmd/haredoc/doc/util.ha | 13-------------
7 files changed, 202 insertions(+), 1156 deletions(-)

diff --git a/cmd/haredoc/doc/color.ha b/cmd/haredoc/doc/color.ha @@ -2,96 +2,107 @@ // (c) Hare authors <https://harelang.org> use ascii; -use fmt; +use hare::unparse; use io; use os; use regex; use strings; -// Syntax type -type syn = enum uint { - NORMAL, - COMMENT, - PRIMARY, - CONSTANT, - FUNCTION, - GLOBAL, - TYPEDEF, - SECONDARY, - KEYWORD, - TYPE, - ATTRIBUTE, - OPERATOR, - PUNCTUATION, - STRING, - NUMBER, -}; - // Colors/Renditions with defaults; SGR parameters for ANSI escape sequences. -let COLORS: [_](str, str) = [ - ("normal" , "0"), - ("comment" , "1"), - ("primary" , "0"), - ("constant" , "0"), - ("function" , "0"), - ("global" , "0"), - ("typedef" , "0"), - ("secondary" , "0"), - ("keyword" , "94"), - ("type" , "96"), - ("attribute" , "33"), - ("operator" , "1"), - ("punctuation" , "0"), - ("string" , "91"), - ("number" , "95"), +let colors: [_]str = [ + "0", // ident + "1", // comment + "0", // constant + "0", // function + "0", // global + "0", // typedef + "0", // import_alias + "0", // secondary + "94", // keyword + "96", // type + "33", // attribute + "1", // operator + "0", // punctuation + "91", // rune_string + "95", // number + "0", // label ]; -fn init_colors() void = { +let normal_color = "0"; +let primary_color = "0"; + +fn init_colors() (void | error) = { const env_colors = os::tryenv("HAREDOC_COLORS", ""); - const expr = regex::compile(`([a-z][a-z]*)=(_|[0-9;]*)`)!; + const expr = regex::compile(`([a-z_]+)=(_|[0-9;]*)`)!; defer regex::finish(&expr); const matches = regex::findall(&expr, env_colors); defer regex::result_freeall(matches); for (let i = 0z; i < len(matches); i += 1) :colors { - const (k, v) = (matches[i][1].content, matches[i][2].content); - - let idx = 0z; - for (let j = 0z; j < len(COLORS); j += 1) { - if (k == COLORS[j].0) { - idx = j; - break; - } else if (j == len(COLORS) - 1) { - fmt::fatalf( - "Error parsing HAREDOC_COLORS, " - "invalid key '{}'", k, - ); - }; - }; - - if (v == "_") { - COLORS[idx] = if (k == "normal") (k, "0") else (k, v); - continue; - }; + let (k, v) = (matches[i][1].content, matches[i][2].content); if (v == "") { continue; }; - COLORS[idx] = (k, v); + let idx = 0z; + let out: *str = switch (k) { + case "ident" => + yield &colors[unparse::synkind::IDENT]; + case "comment" => + yield &colors[unparse::synkind::COMMENT]; + case "constant" => + yield &colors[unparse::synkind::CONSTANT]; + case "function" => + yield &colors[unparse::synkind::FUNCTION]; + case "global" => + yield &colors[unparse::synkind::GLOBAL]; + case "typedef" => + yield &colors[unparse::synkind::TYPEDEF]; + case "import_alias" => + yield &colors[unparse::synkind::IMPORT_ALIAS]; + case "secondary" => + yield &colors[unparse::synkind::SECONDARY]; + case "keyword" => + yield &colors[unparse::synkind::KEYWORD]; + case "type" => + yield &colors[unparse::synkind::TYPE]; + case "attribute" => + yield &colors[unparse::synkind::ATTRIBUTE]; + case "operator" => + yield &colors[unparse::synkind::OPERATOR]; + case "punctuation" => + yield &colors[unparse::synkind::PUNCTUATION]; + case "rune_string" => + yield &colors[unparse::synkind::RUNE_STRING]; + case "number" => + yield &colors[unparse::synkind::NUMBER]; + case "label" => + yield &colors[unparse::synkind::LABEL]; + case "normal" => + yield &normal_color; + case "primary" => + yield &primary_color; + case => + return k: haredoc_colors_error; + }; + + *out = if (v == "_" && k != "normal") "0" else v; }; }; -fn render(h: io::handle, syntax: syn) (size | io::error) = { - if (COLORS[syntax].1 == "_") { - syntax = switch (syntax) { - case syn::CONSTANT, syn::FUNCTION, syn::GLOBAL, syn::TYPEDEF => - yield if (COLORS[syn::PRIMARY].1 == "_") syn::NORMAL - else syn::PRIMARY; - case => - yield syn::NORMAL; - }; +fn color(kind: unparse::synkind) str = { + if (colors[kind] != "_") { + return colors[kind]; + }; + switch (kind) { + case unparse::synkind::CONSTANT, + unparse::synkind::FUNCTION, + unparse::synkind::GLOBAL, + unparse::synkind::TYPEDEF => + return primary_color; + case => + return normal_color; }; - return fmt::fprintf(h, "\x1b[0;{}m", COLORS[syntax].1)?; }; diff --git a/cmd/haredoc/doc/hare.ha b/cmd/haredoc/doc/hare.ha @@ -4,7 +4,6 @@ use bufio; use fmt; use hare::ast; -use hare::lex; use hare::module; use hare::unparse; use io; @@ -114,87 +113,7 @@ fn details_hare(ctx: *context, decl: ast::decl) (void | error) = { }; }; - unparse_hare(ctx.out, decl)?; + unparse::decl(ctx.out, &unparse::syn_wrap, decl)?; fmt::fprintln(ctx.out)?; return; }; - -// Forked from [[hare::unparse]] -fn unparse_hare(out: io::handle, d: ast::decl) (size | io::error) = { - let n = 0z; - match (d.decl) { - case let g: []ast::decl_global => - n += fmt::fprint(out, - if (g[0].is_const) "const " 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)?; - match (g[i]._type) { - case null => - yield; - case let ty: *ast::_type => - n += fmt::fprint(out, ": ")?; - n += unparse::_type(out, - &unparse::syn_nowrap, *ty)?; - }; - if (i + 1 < len(g)) { - n += fmt::fprint(out, ", ")?; - }; - }; - case let 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, - &unparse::syn_nowrap, t[i]._type)?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, ", ")?; - }; - }; - case let c: []ast::decl_const => - n += fmt::fprint(out, "def ")?; - for (let i = 0z; i < len(c); i += 1) { - n += unparse::ident(out, c[i].ident)?; - n += fmt::fprint(out, ": ")?; - match (c[i]._type) { - case null => - yield; - case let ty: *ast::_type => - n += fmt::fprint(out, ": ")?; - n += unparse::_type(out, - &unparse::syn_nowrap, *ty)?; - }; - if (i + 1 < len(c)) { - n += fmt::fprint(out, ", ")?; - }; - }; - case let f: ast::decl_func => - n += fmt::fprint(out, switch (f.attrs) { - case ast::fndecl_attrs::NONE => - yield ""; - case ast::fndecl_attrs::FINI => - yield "@fini "; - case ast::fndecl_attrs::INIT => - yield "@init "; - case ast::fndecl_attrs::TEST => - yield "@test "; - })?; - let p = f.prototype.repr as ast::func_type; - if (len(f.symbol) != 0) { - n += fmt::fprintf(out, "@symbol(\"{}\") ", - f.symbol)?; - }; - n += fmt::fprint(out, "fn ")?; - n += unparse::ident(out, f.ident)?; - n += unparse::prototype(&unparse::context { - out = out, - ... - }, &unparse::syn_nowrap, f.prototype.repr as ast::func_type)?; - }; - n += fmt::fprint(out, ";")?; - return n; -}; diff --git a/cmd/haredoc/doc/html.ha b/cmd/haredoc/doc/html.ha @@ -5,16 +5,12 @@ use encoding::utf8; use fmt; use hare::ast; -use hare::ast::{variadism}; -use hare::lex; -use hare::module; use hare::parse::doc; use hare::unparse; use io; use memio; use net::ip; use net::uri; -use os; use path; use strings; @@ -166,32 +162,6 @@ export fn emit_html(ctx: *context) (void | error) = { }; }; -fn comment_html(out: io::handle, s: str) (size | io::error) = { - // TODO: handle [[references]] - let z = fmt::fprint(out, "<span class='comment'>//")?; - z += html_escape(out, s)?; - z += fmt::fprint(out, "</span><br>")?; - return z; -}; - -fn docs_html(out: io::handle, s: str, indent: size) (size | io::error) = { - const iter = strings::tokenize(s, "\n"); - let z = 0z; - for (true) match (strings::next_token(&iter)) { - case let s: str => - if (!(strings::peek_token(&iter) is void)) { - z += comment_html(out, s)?; - for (let i = 0z; i < indent; i += 1) { - z += fmt::fprint(out, "\t")?; - }; - }; - case void => - break; - }; - - return z; -}; - fn tocentries( out: io::handle, decls: []ast::decl, @@ -213,59 +183,12 @@ fn tocentries( lname)?; undoc = true; }; - tocentry(out, decls[i])?; + unparse::decl(out, &syn_centry, decls[i])?; }; fmt::fprint(out, "</pre>")?; return; }; -fn tocentry(out: io::handle, decl: ast::decl) (void | error) = { - fmt::fprintf(out, "{} ", - match (decl.decl) { - case ast::decl_func => - yield "fn"; - case []ast::decl_type => - yield "type"; - case []ast::decl_const => - yield "const"; - case []ast::decl_global => - yield "let"; - })?; - fmt::fprintf(out, "<a href='#")?; - unparse::ident(out, decl_ident(decl))?; - fmt::fprintf(out, "'>")?; - unparse::ident(out, decl_ident(decl))?; - fmt::fprint(out, "</a>")?; - - match (decl.decl) { - case let t: []ast::decl_type => void; - case let g: []ast::decl_global => - let g = g[0]; - match (g._type) { - case null => - yield; - case let ty: *ast::_type => - fmt::fprint(out, ": ")?; - type_html(out, 0, *ty, true)?; - }; - case let c: []ast::decl_const => - let c = c[0]; - match (c._type) { - case null => - yield; - case let ty: *ast::_type => - fmt::fprint(out, ": ")?; - type_html(out, 0, *ty, true)?; - }; - case let f: ast::decl_func => - prototype_html(out, 0, - f.prototype.repr as ast::func_type, - true)?; - }; - fmt::fprintln(out, ";")?; - return; -}; - fn details(ctx: *context, decl: ast::decl) (void | error) = { fmt::fprintln(ctx.out, "<section class='member'>")?; fmt::fprint(ctx.out, "<h4 id='")?; @@ -295,7 +218,7 @@ fn details(ctx: *context, decl: ast::decl) (void | error) = { }; fmt::fprintln(ctx.out, "<pre class='decl'>")?; - unparse_html(ctx.out, decl)?; + unparse::decl(ctx.out, &syn_html, decl)?; fmt::fprintln(ctx.out, "</pre>")?; if (len(decl.docs) != 0) { @@ -416,409 +339,63 @@ fn markup_html(ctx: *context, in: io::handle) (void | error) = { return; }; -// Forked from [[hare::unparse]] -fn unparse_html(out: io::handle, d: ast::decl) (size | io::error) = { - let n = 0z; - match (d.decl) { - case let c: []ast::decl_const => - n += fmt::fprintf(out, "<span class='keyword'>def</span> ")?; - for (let i = 0z; i < len(c); i += 1) { - n += unparse::ident(out, c[i].ident)?; - match (c[i]._type) { - case null => - yield; - case let ty: *ast::_type => - n += fmt::fprint(out, ": ")?; - n += type_html(out, 0, *ty, false)?; - }; - if (i + 1 < len(c)) { - n += fmt::fprint(out, ", ")?; - }; - }; - case let g: []ast::decl_global => - n += fmt::fprintf(out, "<span class='keyword'>{}</span>", - if (g[0].is_const) "const " else "let ")?; - for (let i = 0z; i < len(g); i += 1) { - n += unparse::ident(out, g[i].ident)?; - match (g[i]._type) { - case null => - yield; - case let ty: *ast::_type => - n += fmt::fprint(out, ": ")?; - n += type_html(out, 0, *ty, false)?; - }; - if (i + 1 < len(g)) { - n += fmt::fprint(out, ", ")?; - }; - }; - case let t: []ast::decl_type => - n += fmt::fprint(out, "<span class='keyword'>type</span> ")?; - for (let i = 0z; i < len(t); i += 1) { - n += unparse::ident(out, t[i].ident)?; - n += fmt::fprint(out, " = ")?; - n += type_html(out, 0, t[i]._type, false)?; - if (i + 1 < len(t)) { - n += fmt::fprint(out, ", ")?; - }; - }; - case let f: ast::decl_func => - n += fmt::fprint(out, switch (f.attrs) { - case ast::fndecl_attrs::NONE => - yield ""; - case ast::fndecl_attrs::FINI => - yield "@fini "; - case ast::fndecl_attrs::INIT => - yield "@init "; - case ast::fndecl_attrs::TEST => - yield "@test "; - })?; - let p = f.prototype.repr as ast::func_type; - n += fmt::fprint(out, "<span class='keyword'>fn</span> ")?; - n += unparse::ident(out, f.ident)?; - n += prototype_html(out, 0, - f.prototype.repr as ast::func_type, - false)?; - }; - n += fmt::fprint(out, ";")?; - return n; -}; - -fn enum_html( - out: io::handle, - indent: size, - t: ast::enum_type +fn syn_centry( + ctx: *unparse::context, + s: str, + kind: unparse::synkind, ) (size | io::error) = { let z = 0z; - - z += fmt::fprint(out, "<span class='type'>enum</span> ")?; - if (t.storage != ast::builtin_type::INT) { - z += fmt::fprintf(out, "<span class='type'>{}</span> ", - unparse::builtin_type(t.storage))?; - }; - z += fmt::fprintln(out, "{")?; - indent += 1; - for (let i = 0z; i < len(t.values); i += 1) { - for (let i = 0z; i < indent; i += 1) { - z += fmt::fprint(out, "\t")?; - }; - const val = t.values[i]; - let wrotedocs = false; - if (val.docs != "") { - // Check if comment should go above or next to field - if (multiline_comment(val.docs)) { - z += docs_html(out, val.docs, indent)?; - wrotedocs = true; - }; - }; - - z += fmt::fprint(out, val.name)?; - - match (val.value) { - case null => void; - case let expr: *ast::expr => - z += fmt::fprint(out, " = ")?; - z += unparse::expr(out, &unparse::syn_nowrap, *expr)?; - }; - - z += fmt::fprint(out, ",")?; - - if (val.docs != "" && !wrotedocs) { - z += fmt::fprint(out, " ")?; - z += docs_html(out, val.docs, 0)?; - } else { - z += fmt::fprintln(out)?; - }; - }; - indent -= 1; - for (let i = 0z; i < indent; i += 1) { - z += fmt::fprint(out, "\t")?; - }; - z += newline(out, indent)?; - z += fmt::fprint(out, "}")?; - return z; -}; - -fn struct_union_html( - out: io::handle, - indent: size, - t: ast::_type, - brief: bool, -) (size | io::error) = { - let z = 0z; - let members = match (t.repr) { - case let t: ast::struct_type => - z += fmt::fprint(out, "<span class='keyword'>struct</span>")?; - if (t.packed) { - z += fmt::fprint(out, " @packed")?; - }; - z += fmt::fprint(out, " {")?; - yield t.members: []ast::struct_member; - case let t: ast::union_type => - z += fmt::fprint(out, "<span class='keyword'>union</span> {")?; - yield t: []ast::struct_member; - }; - - indent += 1; - for (let i = 0z; i < len(members); i += 1) { - const member = members[i]; - - z += newline(out, indent)?; - if (member.docs != "" && !brief) { - z += docs_html(out, member.docs, indent)?; - }; - match (member._offset) { - case null => void; - case let expr: *ast::expr => - z += fmt::fprint(out, "@offset(")?; - z += unparse::expr(out, &unparse::syn_nowrap, *expr)?; - z += fmt::fprint(out, ") ")?; - }; - - match (member.member) { - case let f: ast::struct_field => - z += fmt::fprintf(out, "{}: ", f.name)?; - z += type_html(out, indent, *f._type, brief)?; - case let embed: ast::struct_embedded => - z += type_html(out, indent, *embed, brief)?; - case let indent: ast::struct_alias => - z += unparse::ident(out, indent)?; - }; - z += fmt::fprint(out, ",")?; + switch (kind) { + case unparse::synkind::CONSTANT, + unparse::synkind::FUNCTION, + unparse::synkind::GLOBAL, + unparse::synkind::TYPEDEF => + z += fmt::fprint(ctx.out, "<a href='#")?; + z += html_escape(ctx.out, s)?; + z += fmt::fprint(ctx.out, "'>")?; + z += html_escape(ctx.out, s)?; + z += fmt::fprint(ctx.out, "</a>")?; + ctx.linelen += len(s); + return z; + case => + return syn_html(ctx, s, kind); }; - - indent -= 1; - z += newline(out, indent)?; - z += fmt::fprint(out, "}")?; - - return z; }; -fn type_html( - out: io::handle, - indent: size, - _type: ast::_type, - brief: bool, +fn syn_html( + ctx: *unparse::context, + s: str, + kind: unparse::synkind, ) (size | io::error) = { - if (brief) { - let buf = memio::dynamic(); - defer io::close(&buf)!; - unparse::_type(&buf, &unparse::syn_nowrap, _type)?; - return html_escape(out, memio::string(&buf)!)?; - }; - - // TODO: More detailed formatter which can find aliases nested deeper in - // other types and highlight more keywords, like const let z = 0z; - - if (_type.flags & ast::type_flag::CONST != 0 - && !(_type.repr is ast::func_type)) { - z += fmt::fprint(out, "<span class='keyword'>const</span> ")?; + const span = switch (kind) { + case unparse::synkind::COMMENT => + const stack = ctx.stack as *unparse::stack; + if (stack.cur is *ast::decl) { + // doc comment is unparsed separately later + return 0z; + }; + z += fmt::fprint(ctx.out, "<span class='comment'>")?; + yield true; + case unparse::synkind::KEYWORD => + z += fmt::fprint(ctx.out, "<span class='keyword'>")?; + yield true; + case unparse::synkind::TYPE => + z += fmt::fprint(ctx.out, "<span class='type'>")?; + yield true; + case => + yield false; + }; + + z += html_escape(ctx.out, s)?; + ctx.linelen += len(s); + + if (span) { + z += fmt::fprint(ctx.out, "</span>")?; }; - - if (_type.flags & ast::type_flag::ERROR != 0) { - if (_type.repr is ast::builtin_type) { - z += fmt::fprint(out, "<span class='type'>!</span>")?; - } else { - z += fmt::fprint(out, "!")?; - }; - }; - - match (_type.repr) { - case let a: ast::alias_type => - if (a.unwrap) { - z += fmt::fprint(out, "...")?; - }; - z += unparse::ident(out, a.ident)?; - case let t: ast::builtin_type => - z += fmt::fprintf(out, "<span class='type'>{}</span>", - unparse::builtin_type(t))?; - case let t: ast::tagged_type => - // rough estimate of current line length - let linelen: size = z + (indent + 1) * 8; - z = 0; - linelen += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - linelen += type_html(out, indent, *t[i], brief)?; - if (i + 1 == len(t)) { - break; - }; - linelen += fmt::fprint(out, " |")?; - // use 72 instead of 80 to give a bit of leeway for long - // type names - if (linelen > 72) { - z += linelen; - linelen = (indent + 1) * 8; - z += fmt::fprintln(out)?; - for (let i = 0z; i < indent; i += 1) { - z += fmt::fprint(out, "\t")?; - }; - } else { - linelen += fmt::fprint(out, " ")?; - }; - }; - z += linelen; - z += fmt::fprint(out, ")")?; - case let t: ast::tuple_type => - // rough estimate of current line length - let linelen: size = z + (indent + 1) * 8; - z = 0; - linelen += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - linelen += type_html(out, indent, *t[i], brief)?; - if (i + 1 == len(t)) { - break; - }; - linelen += fmt::fprint(out, ",")?; - // use 72 instead of 80 to give a bit of leeway for long - // type names - if (linelen > 72) { - z += linelen; - linelen = (indent + 1) * 8; - z += fmt::fprintln(out)?; - for (let i = 0z; i < indent; i += 1) { - z += fmt::fprint(out, "\t")?; - }; - } else { - linelen += fmt::fprint(out, " ")?; - }; - }; - z += linelen; - z += fmt::fprint(out, ")")?; - case let t: ast::pointer_type => - if (t.flags & ast::pointer_flag::NULLABLE != 0) { - z += fmt::fprint(out, "<span class='type'>nullable</span> ")?; - }; - z += fmt::fprint(out, "*")?; - z += type_html(out, indent, *t.referent, brief)?; - case let t: ast::func_type => - z += fmt::fprint(out, "<span class='keyword'>fn</span>(")?; - for (let i = 0z; i < len(t.params); i += 1) { - const param = t.params[i]; - z += fmt::fprintf(out, "{}: ", - if (len(param.name) == 0) "_" else param.name)?; - z += type_html(out, indent, *param._type, brief)?; - - if (i + 1 == len(t.params) - && t.variadism == ast::variadism::HARE) { - // TODO: Highlight that as well - z += fmt::fprint(out, "...")?; - }; - if (i + 1 < len(t.params)) { - z += fmt::fprint(out, ", ")?; - }; - }; - if (t.variadism == ast::variadism::C) { - z += fmt::fprint(out, ", ...")?; - }; - z += fmt::fprint(out, ") ")?; - z += type_html(out, indent, *t.result, brief)?; - case let t: ast::enum_type => - z += enum_html(out, indent, t)?; - case let t: ast::list_type => - z += fmt::fprint(out, "[")?; - match (t.length) { - case let expr: *ast::expr => - z += unparse::expr(out, &unparse::syn_nowrap, *expr)?; - case ast::len_slice => - z += 0; - case ast::len_unbounded => - z += fmt::fprintf(out, "*")?; - case ast::len_contextual => - z += fmt::fprintf(out, "_")?; - }; - z += fmt::fprint(out, "]")?; - - z += type_html(out, indent, *t.members, brief)?; - case let t: ast::struct_type => - z += struct_union_html(out, indent, _type, brief)?; - case let t: ast::union_type => - z += struct_union_html(out, indent, _type, brief)?; - }; - return z; }; -fn prototype_html( - out: io::handle, - indent: size, - t: ast::func_type, - brief: bool, -) (size | io::error) = { - let n = 0z; - n += fmt::fprint(out, "(")?; - - // estimate length of prototype to determine if it should span multiple - // lines - const linelen = if (len(t.params) == 0 || brief) { - yield 0z; // If no parameters or brief, only use one line. - } else { - let linelen = indent * 8 + 5; - linelen += if (len(t.params) != 0) len(t.params) * 3 - 1 else 0; - for (let i = 0z; i < len(t.params); i += 1) { - const param = t.params[i]; - linelen += unparse::_type(io::empty, - &unparse::syn_nowrap, *param._type)?; - linelen += if (param.name == "") 1 else len(param.name); - }; - switch (t.variadism) { - case variadism::NONE => void; - case variadism::HARE => - linelen += 3; - case variadism::C => - linelen += 5; - }; - linelen += unparse::_type(io::empty, - &unparse::syn_nowrap, *t.result)?; - yield linelen; - }; - - // use 72 instead of 80 to give a bit of leeway for preceding text - if (linelen > 72) { - indent += 1; - for (let i = 0z; i < len(t.params); i += 1) { - const param = t.params[i]; - n += newline(out, indent)?; - n += fmt::fprintf(out, "{}: ", - if (param.name == "") "_" else param.name)?; - n += type_html(out, indent, *param._type, brief)?; - if (i + 1 == len(t.params) - && t.variadism == variadism::HARE) { - n += fmt::fprint(out, "...")?; - } else { - n += fmt::fprint(out, ",")?; - }; - }; - if (t.variadism == variadism::C) { - n += newline(out, indent)?; - n += fmt::fprint(out, "...")?; - }; - indent -= 1; - n += newline(out, indent)?; - } else for (let i = 0z; i < len(t.params); i += 1) { - const param = t.params[i]; - if (!brief) { - n += fmt::fprintf(out, "{}: ", - if (param.name == "") "_" else param.name)?; - }; - n += type_html(out, indent, *param._type, brief)?; - if (i + 1 == len(t.params)) { - switch (t.variadism) { - case variadism::NONE => void; - case variadism::HARE => - n += fmt::fprint(out, "...")?; - case variadism::C => - n += fmt::fprint(out, ", ...")?; - }; - } else { - n += fmt::fprint(out, ", ")?; - }; - }; - - n += fmt::fprint(out, ") ")?; - n += type_html(out, indent, *t.result, brief)?; - return n; -}; - fn breadcrumb(ident: ast::ident) str = { if (len(ident) == 0) { return ""; diff --git a/cmd/haredoc/doc/sort.ha b/cmd/haredoc/doc/sort.ha @@ -5,10 +5,15 @@ use hare::ast; use sort; use strings; -// Sorts declarations by removing unexported declarations, moving undocumented -// declarations to the end, sorting by identifier, and ensuring that only one -// member is present in each declaration (so that "let x: int = 10, y: int = 20" -// becomes two declarations: "let x: int = 10; let y: int = 20;"). +// Sorts declarations by: +// - removing unexported declarations, +// - setting the "exported" field of all remaining declarations to false, so the +// "export" keyword isn't unparsed, +// - moving undocumented declarations to the end, +// - sorting by identifier, +// - removing the initializer from globals and the body from functions, +// - ensuring that only one member is present in each declaration: +// "let x: int, y: int;" becomes two declarations: "let x: int; let y: int;". export fn sort_decls(decls: []ast::decl) summary = { let sorted = summary { ... }; @@ -20,7 +25,19 @@ export fn sort_decls(decls: []ast::decl) summary = { match (decl.decl) { case let f: ast::decl_func => - append(sorted.funcs, decl); + append(sorted.funcs, ast::decl { + exported = false, + start = decl.start, + end = decl.end, + decl = ast::decl_func { + symbol = f.symbol, + ident = f.ident, + prototype = f.prototype, + body = void, + attrs = f.attrs, + }, + docs = decl.docs, + }); case let t: []ast::decl_type => for (let j = 0z; j < len(t); j += 1) { let bucket = &sorted.types; @@ -28,7 +45,7 @@ export fn sort_decls(decls: []ast::decl) summary = { bucket = &sorted.errors; }; append(bucket, ast::decl { - exported = true, + exported = false, start = decl.start, end = decl.end, decl = alloc([t[j]]), @@ -38,7 +55,7 @@ export fn sort_decls(decls: []ast::decl) summary = { case let c: []ast::decl_const => for (let j = 0z; j < len(c); j += 1) { append(sorted.constants, ast::decl { - exported = true, + exported = false, start = decl.start, end = decl.end, decl = alloc([c[j]]), @@ -48,10 +65,17 @@ export fn sort_decls(decls: []ast::decl) summary = { case let g: []ast::decl_global => for (let j = 0z; j < len(g); j += 1) { append(sorted.globals, ast::decl { - exported = true, + exported = false, start = decl.start, end = decl.end, - decl = alloc([g[j]]), + decl = alloc([ast::decl_global { + is_const = g[j].is_const, + is_threadlocal = g[j].is_threadlocal, + symbol = g[j].symbol, + ident = g[j].ident, + _type = g[j]._type, + init = null, + }]), docs = decl.docs, }); }; diff --git a/cmd/haredoc/doc/tty.ha b/cmd/haredoc/doc/tty.ha @@ -5,31 +5,32 @@ use ascii; use bufio; use fmt; use hare::ast; -use hare::ast::{variadism}; -use hare::lex; use hare::unparse; use io; use memio; use os; use strings; +use types; let firstline: bool = true; // Formats output as Hare source code (prototypes) with syntax highlighting export fn emit_tty(ctx: *context) (void | error) = { - init_colors(); + init_colors()?; const summary = ctx.summary; match (ctx.readme) { case let readme: io::file => - for (true) match (bufio::read_line(readme)?) { - case io::EOF => - break; - case let b: []u8 => - defer free(b); + let rbuf: [os::BUFSZ]u8 = [0...]; + let readme = bufio::init(readme, rbuf, []); + let sc = bufio::newscanner(&readme, types::SIZE_MAX); + defer bufio::finish(&sc); + for (true) match (bufio::scan_line(&sc)?) { + case io::EOF => break; + case let s: const str => firstline = false; - insert(b[0], ' '); - comment_tty(ctx.out, strings::fromutf8(b)!)?; + fmt::fprintfln(ctx.out, "\x1b[{}m" "// {}" "\x1b[m", + color(unparse::synkind::COMMENT), s)?; }; case void => void; }; @@ -57,14 +58,12 @@ export fn emit_tty(ctx: *context) (void | error) = { fn emit_submodules_tty(ctx: *context) (void | error) = { if (len(ctx.submods) != 0) { fmt::fprintln(ctx.out)?; + fmt::fprintf(ctx.out, "\x1b[{}m", + color(unparse::synkind::COMMENT))?; if (len(ctx.ident) == 0) { - render(ctx.out, syn::COMMENT)?; fmt::fprintln(ctx.out, "// Modules")?; - render(ctx.out, syn::NORMAL)?; } else { - render(ctx.out, syn::COMMENT)?; fmt::fprintln(ctx.out, "// Submodules")?; - render(ctx.out, syn::NORMAL)?; }; for (let i = 0z; i < len(ctx.submods); i += 1) { let submodule = if (len(ctx.ident) != 0) { @@ -76,521 +75,33 @@ fn emit_submodules_tty(ctx: *context) (void | error) = { }; defer free(submodule); - render(ctx.out, syn::COMMENT)?; fmt::fprintfln(ctx.out, "// - [[{}]]", submodule)?; - render(ctx.out, syn::NORMAL)?; }; }; }; -fn comment_tty(out: io::handle, s: str) (size | io::error) = { - let n = 0z; - n += render(out, syn::COMMENT)?; - n += fmt::fprintfln(out, "//{}", s)?; - n += render(out, syn::NORMAL)?; - return n; -}; - -fn docs_tty(out: io::handle, s: str, indent: size) (size | io::error) = { - const iter = strings::tokenize(s, "\n"); - let z = 0z; - for (true) match (strings::next_token(&iter)) { - case let s: str => - if (!(strings::peek_token(&iter) is void)) { - z += comment_tty(out, s)?; - for (let i = 0z; i < indent; i += 1) { - z += fmt::fprint(out, "\t")?; - }; - }; - case void => - break; - }; - - return z; -}; - -fn isws(s: str) bool = { - const iter = strings::iter(s); - for (true) { - match (strings::next(&iter)) { - case let r: rune => - if (!ascii::isspace(r)) { - return false; - }; - case void => - break; - }; - }; - return true; -}; - fn details_tty(ctx: *context, decl: ast::decl) (void | error) = { if (len(decl.docs) == 0 && !ctx.show_undocumented) { return; }; + fmt::fprint(ctx.out, "\x1b[m")?; // reset styling if (!firstline) { fmt::fprintln(ctx.out)?; }; firstline = false; - docs_tty(ctx.out, decl.docs, 0)?; - unparse_tty(ctx.out, decl)?; + unparse::decl(ctx.out, &syn_tty, decl)?; fmt::fprintln(ctx.out)?; }; -// Forked from [[hare::unparse]] -fn unparse_tty(out: io::handle, d: ast::decl) (size | io::error) = { - let n = 0z; - match (d.decl) { - case let g: []ast::decl_global => - n += render(out, syn::KEYWORD)?; - n += fmt::fprint(out, if (g[0].is_const) "const " else "let ")?; - for (let i = 0z; i < len(g); i += 1) { - if (len(g[i].symbol) != 0) { - n += render(out, syn::ATTRIBUTE)?; - n += fmt::fprintf(out, "@symbol(")?; - n += render(out, syn::STRING)?; - n += fmt::fprintf(out, `"{}"`, g[i].symbol)?; - n += render(out, syn::ATTRIBUTE)?; - n += fmt::fprintf(out, ") ")?; - n += render(out, syn::NORMAL)?; - }; - n += render(out, syn::GLOBAL)?; - n += unparse::ident(out, g[i].ident)?; - match (g[i]._type) { - case null => - yield; - case let ty: *ast::_type => - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ": ")?; - n += type_tty(out, 0, *ty)?; - }; - if (i + 1 < len(g)) { - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ", ")?; - }; - n += render(out, syn::NORMAL)?; - }; - case let c: []ast::decl_const => - n += render(out, syn::KEYWORD)?; - n += fmt::fprintf(out, "def ")?; - for (let i = 0z; i < len(c); i += 1) { - n += render(out, syn::CONSTANT)?; - n += unparse::ident(out, c[i].ident)?; - n += render(out, syn::PUNCTUATION)?; - match (c[i]._type) { - case null => - yield; - case let ty: *ast::_type => - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ": ")?; - n += type_tty(out, 0, *ty)?; - }; - if (i + 1 < len(c)) { - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ", ")?; - }; - }; - case let t: []ast::decl_type => - n += render(out, syn::KEYWORD)?; - n += fmt::fprint(out, "type ")?; - for (let i = 0z; i < len(t); i += 1) { - n += render(out, syn::TYPEDEF)?; - n += unparse::ident(out, t[i].ident)?; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, " = ")?; - n += type_tty(out, 0, t[i]._type)?; - if (i + 1 < len(t)) { - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ", ")?; - }; - }; - case let f: ast::decl_func => - n += render(out, syn::ATTRIBUTE)?; - n += fmt::fprint(out, switch (f.attrs) { - case ast::fndecl_attrs::NONE => - yield ""; - case ast::fndecl_attrs::FINI => - yield "@fini "; - case ast::fndecl_attrs::INIT => - yield "@init "; - case ast::fndecl_attrs::TEST => - yield "@test "; - })?; - n += render(out, syn::NORMAL)?; - - let p = f.prototype.repr as ast::func_type; - if (len(f.symbol) != 0) { - n += render(out, syn::ATTRIBUTE)?; - n += fmt::fprintf(out, "@symbol(")?; - n += render(out, syn::STRING)?; - n += fmt::fprintf(out, `"{}"`, f.symbol)?; - n += render(out, syn::ATTRIBUTE)?; - n += fmt::fprintf(out, ") ")?; - n += render(out, syn::NORMAL)?; - }; - n += render(out, syn::KEYWORD)?; - n += fmt::fprint(out, "fn ")?; - n += render(out, syn::FUNCTION)?; - n += unparse::ident(out, f.ident)?; - n += fmt::fprint(out, "\x1b[0m")?; - n += prototype_tty(out, 0, - f.prototype.repr as ast::func_type)?; - }; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ";")?; - return n; -}; - -fn prototype_tty( - out: io::handle, - indent: size, - t: ast::func_type, +fn syn_tty( + ctx: *unparse::context, + s: str, + kind: unparse::synkind, ) (size | io::error) = { - let n = 0z; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, "(")?; - - let typenames: []str = []; - // TODO: https://todo.sr.ht/~sircmpwn/hare/581 - if (len(t.params) > 0) { - typenames = alloc([""...], len(t.params)); - }; - defer strings::freeall(typenames); - let retname = ""; - defer free(retname); - - // estimate length of prototype to determine if it should span multiple - // lines - const linelen = if (len(t.params) == 0) { - let strm = memio::dynamic(); - defer io::close(&strm)!; - type_tty(&strm, indent, *t.result)?; - retname = strings::dup(memio::string(&strm)!); - yield 0z; // only use one line if there's no parameters - } else { - let strm = memio::dynamic(); - defer io::close(&strm)!; - let linelen = indent * 8 + 5; - linelen += if (len(t.params) != 0) len(t.params) * 3 - 1 else 0; - for (let i = 0z; i < len(t.params); i += 1) { - const param = t.params[i]; - linelen += unparse::_type(&strm, - &unparse::syn_nowrap, *param._type)?; - typenames[i] = strings::dup(memio::string(&strm)!); - linelen += if (param.name == "") 1 else len(param.name); - memio::reset(&strm); - }; - switch (t.variadism) { - case variadism::NONE => void; - case variadism::HARE => - linelen += 3; - case variadism::C => - linelen += 5; - }; - linelen += type_tty(&strm, indent, *t.result)?; - retname = strings::dup(memio::string(&strm)!); - yield linelen; - }; - - // use 72 instead of 80 to give a bit of leeway for preceding text - if (linelen > 72) { - indent += 1; - for (let i = 0z; i < len(t.params); i += 1) { - const param = t.params[i]; - n += newline(out, indent)?; - n += render(out, syn::SECONDARY)?; - n += fmt::fprint(out, - if (param.name == "") "_" else param.name)?; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ": ")?; - n += render(out, syn::TYPE)?; - n += fmt::fprint(out, typenames[i])?; - if (i + 1 == len(t.params) - && t.variadism == variadism::HARE) { - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, "...")?; - } else { - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ",")?; - }; - }; - if (t.variadism == variadism::C) { - n += newline(out, indent)?; - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, "...")?; - }; - indent -= 1; - n += newline(out, indent)?; - } else for (let i = 0z; i < len(t.params); i += 1) { - const param = t.params[i]; - n += render(out, syn::SECONDARY)?; - n += fmt::fprint(out, - if (param.name == "") "_" else param.name)?; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ": ")?; - n += render(out, syn::TYPE)?; - n += fmt::fprint(out, typenames[i])?; - if (i + 1 == len(t.params)) { - switch (t.variadism) { - case variadism::NONE => void; - case variadism::HARE => - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, "...")?; - case variadism::C => - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ", ")?; - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, "...")?; - }; - } else { - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ", ")?; - }; - }; - - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ")", retname)?; - return n; -}; - -// Forked from [[hare::unparse]] -fn struct_union_type_tty( - out: io::handle, - indent: size, - t: ast::_type, -) (size | io::error) = { - let n = 0z; - let membs = match (t.repr) { - case let st: ast::struct_type => - n += render(out, syn::TYPE)?; - n += fmt::fprint(out, "struct")?; - if (st.packed) { - n += render(out, syn::ATTRIBUTE)?; - n += fmt::fprint(out, " @packed")?; - }; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, " {")?; - yield st.members: []ast::struct_member; - case let ut: ast::union_type => - n += render(out, syn::TYPE)?; - n += fmt::fprint(out, "union")?; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, " {")?; - yield ut: []ast::struct_member; - }; - - indent += 1z; - for (let i = 0z; i < len(membs); i += 1) { - n += newline(out, indent)?; - if (membs[i].docs != "") { - n += docs_tty(out, membs[i].docs, indent)?; - }; - - match (membs[i]._offset) { - case null => void; - case let ex: *ast::expr => - n += render(out, syn::ATTRIBUTE)?; - n += fmt::fprint(out, "@offset(")?; - n += render(out, syn::NUMBER)?; - n += unparse::expr(out, &unparse::syn_nowrap, *ex)?; - n += render(out, syn::ATTRIBUTE)?; - n += fmt::fprint(out, ")")?; - n += render(out, syn::NORMAL)?; - }; - - match (membs[i].member) { - case let se: ast::struct_embedded => - n += type_tty(out, indent, *se)?; - case let sa: ast::struct_alias => - n += unparse::ident(out, sa)?; - case let sf: ast::struct_field => - n += render(out, syn::SECONDARY)?; - n += fmt::fprint(out, sf.name)?; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ": ")?; - n += type_tty(out, indent, *sf._type)?; - }; - - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ",")?; - }; - - indent -= 1; - n += newline(out, indent)?; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, "}")?; - return n; -}; - -// Forked from [[hare::unparse]] -fn type_tty( - out: io::handle, - indent: size, - t: ast::_type, -) (size | io::error) = { - let n = 0z; - if (t.flags & ast::type_flag::CONST != 0 - && !(t.repr is ast::func_type)) { - n += render(out, syn::TYPE)?; - n += fmt::fprint(out, "const ")?; - }; - if (t.flags & ast::type_flag::ERROR != 0) { - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, "!")?; - }; - - match (t.repr) { - case let a: ast::alias_type => - if (a.unwrap) { - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, "...")?; - }; - n += render(out, syn::TYPE)?; - n += unparse::ident(out, a.ident)?; - case let b: ast::builtin_type => - n += render(out, syn::TYPE)?; - n += fmt::fprintf(out, "{}", unparse::builtin_type(b))?; - case let e: ast::enum_type => - n += render(out, syn::TYPE)?; - n += fmt::fprint(out, "enum ")?; - if (e.storage != ast::builtin_type::INT) { - n += fmt::fprintf(out, - "{} ", unparse::builtin_type(e.storage))?; - }; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprintln(out, "{")?; - indent += 1; - for (let i = 0z; i < len(e.values); i += 1) { - for (let i = 0z; i < indent; i += 1) { - n += fmt::fprint(out, "\t")?; - }; - let value = e.values[i]; - let wrotedocs = false; - if (value.docs != "") { - // Check if comment should go above or next to - // field - if (multiline_comment(value.docs)) { - n += docs_tty(out, value.docs, indent)?; - wrotedocs = true; - }; - }; - n += render(out, syn::SECONDARY)?; - n += fmt::fprint(out, value.name)?; - match (value.value) { - case null => void; - case let e: *ast::expr => - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, " = ")?; - n += render(out, syn::NORMAL)?; - n += unparse::expr(out, - &unparse::syn_nowrap, *e)?; - }; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ",")?; - if (value.docs != "" && !wrotedocs) { - n += fmt::fprint(out, " ")?; - n += docs_tty(out, value.docs, 0)?; - } else { - n += fmt::fprintln(out)?; - }; - }; - indent -= 1; - for (let i = 0z; i < indent; i += 1) { - n += fmt::fprint(out, "\t")?; - }; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, "}")?; - case let f: ast::func_type => - n += render(out, syn::TYPE)?; - n += fmt::fprint(out, "fn")?; - n += prototype_tty(out, indent, f)?; - case let l: ast::list_type => - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, "[")?; - match (l.length) { - case ast::len_slice => void; - case ast::len_unbounded => - n += fmt::fprint(out, "*")?; - case ast::len_contextual => - n += fmt::fprint(out, "_")?; - case let e: *ast::expr => - n += unparse::expr(out, &unparse::syn_nowrap, *e)?; - }; - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, "]")?; - n += type_tty(out, indent, *l.members)?; - case let p: ast::pointer_type => - if (p.flags & ast::pointer_flag::NULLABLE != 0) { - n += render(out, syn::TYPE)?; - n += fmt::fprint(out, "nullable ")?; - }; - n += render(out, syn::OPERATOR)?; - n += fmt::fprint(out, "*")?; - n += type_tty(out, indent, *p.referent)?; - case ast::struct_type => - n += struct_union_type_tty(out, indent, t)?; - case ast::union_type => - n += struct_union_type_tty(out, indent, t)?; - case let t: ast::tagged_type => - // rough estimate of current line length - let linelen: size = n + (indent + 1) * 8; - n = 0; - n += render(out, syn::PUNCTUATION)?; - linelen += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - linelen += type_tty(out, indent, *t[i])?; - if (i + 1 == len(t)) { - break; - }; - n += render(out, syn::PUNCTUATION)?; - linelen += fmt::fprint(out, " |")?; - // use 72 instead of 80 to give a bit of leeway for long - // type names - if (linelen > 72) { - n += linelen; - linelen = (indent + 1) * 8; - n += fmt::fprintln(out)?; - for (let i = 0z; i <= indent; i += 1) { - n += fmt::fprint(out, "\t")?; - }; - } else { - linelen += fmt::fprint(out, " ")?; - }; - }; - n += linelen; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ")")?; - case let t: ast::tuple_type => - // rough estimate of current line length - let linelen: size = n + (indent + 1) * 8; - n = 0; - n += render(out, syn::PUNCTUATION)?; - linelen += fmt::fprint(out, "(")?; - for (let i = 0z; i < len(t); i += 1) { - linelen += type_tty(out, indent, *t[i])?; - if (i + 1 == len(t)) { - break; - }; - n += render(out, syn::PUNCTUATION)?; - linelen += fmt::fprint(out, ",")?; - // use 72 instead of 80 to give a bit of leeway for long - // type names - if (linelen > 72) { - n += linelen; - linelen = (indent + 1) * 8; - n += fmt::fprintln(out)?; - for (let i = 0z; i <= indent; i += 1) { - n += fmt::fprint(out, "\t")?; - }; - } else { - linelen += fmt::fprint(out, " ")?; - }; - }; - n += linelen; - n += render(out, syn::PUNCTUATION)?; - n += fmt::fprint(out, ")")?; - }; - return n; + let z = fmt::fprintf(ctx.out, "\x1b[{}m", color(kind))?; + z += unparse::syn_wrap(ctx, s, kind)?; + z += fmt::fprint(ctx.out, "\x1b[m")?; + return z; }; diff --git a/cmd/haredoc/doc/types.ha b/cmd/haredoc/doc/types.ha @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only // (c) Hare authors <https://harelang.org> +use fmt; use fs; use hare::ast; use hare::lex; @@ -9,7 +10,12 @@ use hare::parse; use io; use os::exec; -export type error = !(lex::error | parse::error | io::error | module::error | exec::error | fs::error); +export type haredoc_colors_error = !str; + +export type error = !(lex::error | parse::error | io::error | module::error | + exec::error | fs::error | haredoc_colors_error); + +def HAREDOC_COLORS_ERROR_MSG = "Error parsing HAREDOC_COLORS: invalid key"; export fn strerror(err: error) str = { match (err) { @@ -21,6 +27,17 @@ export fn strerror(err: error) str = { return io::strerror(err); case let err: module::error => return module::strerror(err); + case let err: exec::error => + return exec::strerror(err); + case let err: fs::error => + return fs::strerror(err); + case let err: haredoc_colors_error => + let buf: [128]u8 = [0...]; + if (len(HAREDOC_COLORS_ERROR_MSG) + len(err) + 3 > len(buf)) { + return HAREDOC_COLORS_ERROR_MSG; + }; + return fmt::bsprintf(buf, "{} '{}'", + HAREDOC_COLORS_ERROR_MSG, err); }; }; diff --git a/cmd/haredoc/doc/util.ha b/cmd/haredoc/doc/util.ha @@ -11,19 +11,6 @@ use sort; use sort::cmp; use strings; -// Forked from [[hare::unparse]]. -fn newline(out: io::handle, indent: size) (size | io::error) = { - let n = 0z; - n += fmt::fprint(out, "\n")?; - for (let i = 0z; i < indent; i += 1) { - n += fmt::fprint(out, "\t")?; - }; - return n; -}; - -fn multiline_comment(s: str) bool = - strings::byteindex(s, '\n') as size != len(s) - 1; - fn trim_comment(s: str) str = { let trimmed = memio::dynamic(); let tok = strings::tokenize(s, "\n");