hare

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

commit c2f0840a0ba79b36f4f03aa41cf31efce57821ab
parent ceca1b49a652baaeb5aa13db801e53964ed1a417
Author: Sebastian <sebastian@sebsite.pw>
Date:   Tue, 17 Oct 2023 22:30:11 -0400

haredoc: show initializer functions for types

For instance, `haredoc memio::stream` also shows all bufio functions
which return a stream.

This doesn't implement anything for HTML, since we plan to drop the HTML
backend anyway.

I opted to use the following rules here:
- Types only show initializer functions if they aren't error types and
  if they aren't aliases of void.
- A function is an "initializer" function if it returns the type, a
  pointer to or slice of the type, or a tagged union containing the
  type.
- Initializer functions are shown whether or not the type is documented.
  My rationale here is 1. if you're specifically looking for docs on one
  type, initializer functions will always be helpful to see, and 2. the
  example of fs::iterator given in the original ticket now has docs, and
  this is true of lots of types that the user still isn't expected to
  directly poke at, such as strings::tokenizer.
- Only initializer functions are shown, not functions which take in the
  type. The original ticket mentioned that fs::iterator should show
  fs::next, and I initially implemented this by checking if the first
  parameter matched the type (using the same rules as for when checking
  a function return type), but this caused way too many additional
  functions to appear, the vast majority of which aren't actually
  helpful. For example, docs for path::buffer would show nearly every
  path:: function, rather than just path::init. If there are certain
  functions that are important to know about for a type, they're most
  likely already explicitly documented anyways.

Implements: https://todo.sr.ht/~sircmpwn/hare/421
Signed-off-by: Sebastian <sebastian@sebsite.pw>

Diffstat:
Mcmd/haredoc/main.ha | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 104 insertions(+), 5 deletions(-)

diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha @@ -154,17 +154,26 @@ fn doc(name: str, cmd: *getopt::command) (void | error) = { ast::imports_finish(u.imports); append(decls, u.decls...); }; - let new: []ast::decl = []; + + let matching: []ast::decl = []; + let notmatching: []ast::decl = []; for (let i = 0z; i < len(decls); i += 1) { if (has_decl(decls[i], id[len(id) - 1])) { - append(new, decls[i]); + append(matching, decls[i]); } else { - ast::decl_finish(decls[i]); + append(notmatching, decls[i]); }; }; + get_init_funcs(&matching, &notmatching); + + for (let i = 0z; i < len(notmatching); i += 1) { + ast::decl_finish(notmatching[i]); + }; + free(notmatching); free(decls); - decls = new; - if (len(new) == 0) { + decls = matching; + + if (len(matching) == 0) { if (modpath == "") { const id = unparse::identstr(id); fmt::fatalf("Could not find {}", id); @@ -372,3 +381,93 @@ fn has_decl(decl: ast::decl, name: str) bool = { assert(parseident("") is void); assert(parseident("::") is void); }; + +fn get_init_funcs(matching: *[]ast::decl, notmatching: *[]ast::decl) void = { + if (len(matching) != 1) { + return; + }; + let ident = match (matching[0].decl) { + case let d: []ast::decl_type => + if (len(d) != 1 || len(d[0].ident) != 1) { + return; + }; + if (d[0]._type.flags & ast::type_flag::ERROR != 0) { + return; + }; + match (d[0]._type.repr) { + case let repr: ast::builtin_type => + if (repr == ast::builtin_type::VOID) { + return; + }; + case => void; + }; + yield d[0].ident; + case => + return; + }; + + for (let i = 0z; i < len(notmatching); i += 1) { + let _type = match (notmatching[i].decl) { + case let d: []ast::decl_const => + yield match (d[0]._type) { + case let t: *ast::_type => + yield t; + case null => + continue; + }; + case let d: []ast::decl_global => + yield match (d[0]._type) { + case let t: *ast::_type => + yield t; + case null => + continue; + }; + case let d: ast::decl_func => + let _type = d.prototype.repr as ast::func_type; + yield _type.result; + case => + continue; + }; + + if (is_init_type(ident, _type)) { + append(matching, notmatching[i]); + delete(notmatching[i]); + i -= 1; + }; + }; +}; + +fn is_init_type(ident: ast::ident, _type: *ast::_type) bool = { + let type_ident = match (_type.repr) { + case let repr: ast::alias_type => + yield repr.ident; + case let repr: ast::list_type => + if (!(repr.length is ast::len_slice)) { + return false; + }; + yield match (repr.members.repr) { + case let repr: ast::alias_type => + yield repr.ident; + case => + return false; + }; + case let repr: ast::pointer_type => + yield match (repr.referent.repr) { + case let repr: ast::alias_type => + yield repr.ident; + case => + return false; + }; + case let repr: ast::tagged_type => + for (let i = 0z; i < len(repr); i += 1) { + if (is_init_type(ident, repr[i])) { + return true; + }; + }; + return false; + case => + return false; + }; + + return ast::ident_eq(ident, type_ident); +};