commit fe6332b75cc8cf3d013da357f340552a7c0cc13c
parent d97c115d356eb2b9d3669f1a4cafe5974ee9a180
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 19 Apr 2021 17:29:46 -0400
haredoc: implement module resolution
Diffstat:
7 files changed, 129 insertions(+), 30 deletions(-)
diff --git a/Makefile b/Makefile
@@ -35,6 +35,7 @@ harec_srcs=\
haredoc_srcs=\
./cmd/haredoc/main.ha \
./cmd/haredoc/errors.ha \
+ ./cmd/haredoc/env.ha \
./cmd/haredoc/hare.ha \
./cmd/haredoc/html.ha \
./cmd/haredoc/sort.ha
diff --git a/cmd/haredoc/env.ha b/cmd/haredoc/env.ha
@@ -0,0 +1,21 @@
+use hare::module;
+use os::exec;
+use os;
+use strings;
+
+fn default_tags() []module::tag = {
+ // TODO: Once os::exec can handle pipes, we should read the default tags
+ // from $(hare version).
+ return alloc([module::tag {
+ name = strings::dup("linux"),
+ mode = module::tag_mode::INCLUSIVE,
+ }, module::tag {
+ name = strings::dup(os::machine()),
+ mode = module::tag_mode::INCLUSIVE,
+ }]);
+};
+
+fn default_harepath() str = {
+ // TODO: Same deal
+ return "/usr/share/src/hare/stdlib:/usr/share/src/hare/third-party";
+};
diff --git a/cmd/haredoc/hare.ha b/cmd/haredoc/hare.ha
@@ -8,8 +8,9 @@ use strings;
use strio;
// Formats output as Hare source code (prototypes)
-fn emit_hare(summary: summary) (void | error) = {
- // TODO: Should we emit the dependencies, too?
+fn emit_hare(ctx: *context) (void | error) = {
+ const summary = ctx.summary;
+ // XXX: Should we emit the dependencies, too?
for (let i = 0z; i < len(summary.types); i += 1) {
details_hare(summary.types[i])?;
};
diff --git a/cmd/haredoc/html.ha b/cmd/haredoc/html.ha
@@ -2,6 +2,7 @@ use fmt;
use format::html;
use hare::ast;
use hare::lex;
+use hare::module;
use hare::unparse;
use io;
use os;
@@ -9,12 +10,32 @@ use strings;
use strio;
// Formats output as HTML
-fn emit_html(decls: summary, template: bool) (void | error) = {
- if (template) head()?;
+fn emit_html(ctx: *context) (void | error) = {
+ const decls = ctx.summary;
+ if (ctx.template) head()?;
+
+ const ident = unparse::identstr(ctx.ident);
+ fmt::printf("<h2>{} <span class='heading-extra'>", ident);
+ for (let i = 0z; i < len(ctx.tags); i += 1) {
+ const mode = switch (ctx.tags[i].mode) {
+ module::tag_mode::INCLUSIVE => '+',
+ module::tag_mode::EXCLUSIVE => '-',
+ };
+ fmt::printf("{}{} ", mode, ctx.tags[i].name);
+ };
+ fmt::println("</span></h2>")?;
+
+ match (ctx.readme) {
+ null => void,
+ f: *io::stream => {
+ // TODO: Format as haredoc markup
+ fmt::println("<div class='readme'>")?;
+ fmt::println("<p>")?;
+ io::copy(os::stdout, f)?;
+ fmt::println("</div>")?;
+ },
+ };
- // TODO: Module name
- fmt::println("<h2>hash::fnv <span class='heading-extra'>+linux +x86_64</span></h2>")?;
- fmt::println("<p class='readme'>TODO: Insert module README here")?;
if (len(decls.types) != 0) {
fmt::println("<h4>Types</h4>")?;
fmt::println("<ol>")?;
diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha
@@ -3,9 +3,11 @@ use fs;
use getopt;
use hare::ast;
use hare::lex;
+use hare::module;
use hare::parse;
use io;
use os;
+use path;
use strings;
type format = enum {
@@ -15,6 +17,16 @@ type format = enum {
GEMTEXT,
};
+type context = struct {
+ ident: ast::ident,
+ tags: []module::tag,
+ version: module::version,
+ summary: summary,
+ format: format,
+ template: bool,
+ readme: nullable *io::stream,
+};
+
export fn main() void = {
// TODO: Use format::TTY by default if stdout is a tty
let fmt = format::HARE;
@@ -41,28 +53,61 @@ export fn main() void = {
};
};
- let unit: []ast::subunit = [];
- defer {
- for (let i = 0z; i < len(unit); i += 1) {
- ast::subunit_free(unit[i]);
- };
- free(unit);
+ let decls: []ast::decl = [];
+ defer free(decls); // TODO: Free interior state
+
+ let tags = default_tags();
+ defer module::tags_free(tags);
+
+ let ctx = module::context_init(tags, [], default_harepath());
+ defer module::context_finish(&ctx);
+
+ assert(len(cmd.args) == 1); // TODO: Something more sophisticated here
+
+ const id = match (parse::identstr(cmd.args[0])) {
+ err: parse::error => fmt::fatal(parse::strerror(err)),
+ id: ast::ident => id,
};
- 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)),
- };
+ const version = match (module::lookup(&ctx, id)) {
+ ver: module::version => ver,
+ err: module::error => fmt::fatal(
+ "Error scanning input module: {}",
+ module::strerror(err)),
};
- for (let i = 0z; i < len(unit); i += 1) {
- let summary = sort_decls(unit[i].decls);
- match (emit(summary, fmt, template)) {
- _: void => void,
+ for (let i = 0z; i < len(version.inputs); i += 1) {
+ const in = version.inputs[i];
+ match (scan(in.path)) {
+ u: ast::subunit => append(decls, u.decls...),
err: error => fmt::fatal("Error: {}", strerror(err)),
};
};
+
+ const rpath = path::join(version.basedir, "README");
+ defer free(rpath);
+ const readme: nullable *io::stream = match (os::open(rpath)) {
+ err: fs::error => null,
+ f: *io::stream => f,
+ };
+ defer match (readme) {
+ null => void,
+ f: *io::stream => io::close(f),
+ };
+
+ let dctx = context {
+ ident = id,
+ tags = tags,
+ version = version,
+ summary = sort_decls(decls),
+ format = fmt,
+ template = template,
+ readme = readme,
+ };
+ match (emit(&dctx)) {
+ _: void => void,
+ err: error => fmt::fatal("Error: {}", strerror(err)),
+ };
};
fn scan(path: str) (ast::subunit | error) = {
@@ -76,13 +121,9 @@ fn scan(path: str) (ast::subunit | error) = {
return parse::subunit(&lexer)?;
};
-fn emit(
- summary: summary,
- fmt: format,
- template: bool,
-) (void | error) = switch (fmt) {
- format::HARE => emit_hare(summary),
+fn emit(ctx: *context) (void | error) = switch (ctx.format) {
+ format::HARE => emit_hare(ctx),
format::TTY => abort(), // TODO
- format::HTML => emit_html(summary, template)?,
+ format::HTML => emit_html(ctx)?,
format::GEMTEXT => abort(), // TODO
};
diff --git a/hare/module/context.ha b/hare/module/context.ha
@@ -71,6 +71,9 @@ export fn context_finish(ctx: *context) void = {
// Converts an identifier to a partial path (e.g. foo::bar becomes foo/bar). The
// return value must be freed by the caller.
export fn identpath(name: ast::ident) str = {
+ if (len(name) == 0) {
+ return strings::dup(".");
+ };
let p = path::join(name[0]);
for (let i = 1z; i < len(name); i += 1) {
let q = path::join(p, name[i]);
diff --git a/hare/parse/ident.ha b/hare/parse/ident.ha
@@ -1,6 +1,9 @@
+use bufio;
use hare::ast;
-use hare::lex;
use hare::lex::{ltok};
+use hare::lex;
+use io;
+use strings;
fn ident_trailing(lexer: *lex::lexer) ((ast::ident, bool) | error) = {
let ident: []str = [];
@@ -32,3 +35,11 @@ export fn ident(lexer: *lex::lexer) (ast::ident | error) = {
synassert(mkloc(lexer), !ident.1, "Unexpected trailing :: in ident")?;
return ident.0;
};
+
+// Parses an identifier from a string.
+export fn identstr(in: str) (ast::ident | error) = {
+ const buf = bufio::fixed(strings::toutf8(in), io::mode::READ);
+ defer io::close(buf);
+ const lexer = lex::init(buf, "<string>");
+ return ident(&lexer);
+};