diff options
| -rw-r--r-- | CHANGES.md | 7 | ||||
| -rw-r--r-- | docs/command-line-parsing.md | 33 | ||||
| -rw-r--r-- | src/ast.c | 17 | ||||
| -rw-r--r-- | src/ast.h | 5 | ||||
| -rw-r--r-- | src/compile/cli.c | 142 | ||||
| -rw-r--r-- | src/compile/cli.h | 3 | ||||
| -rw-r--r-- | src/compile/files.c | 1 | ||||
| -rw-r--r-- | src/compile/statements.c | 1 | ||||
| -rw-r--r-- | src/parse/expressions.c | 2 | ||||
| -rw-r--r-- | src/parse/files.c | 41 | ||||
| -rw-r--r-- | src/parse/text.c | 12 | ||||
| -rw-r--r-- | src/parse/text.h | 4 | ||||
| -rw-r--r-- | src/tomo.c | 6 | ||||
| -rw-r--r-- | src/typecheck.c | 7 |
14 files changed, 196 insertions, 85 deletions
@@ -12,6 +12,13 @@ - Syntax for text literals and inline C code has been simplified somewhat. - Syntax for tables has changed to use colons (`{k: v}`) instead of equals (`{k=v}`). +- Added metadata format instead of `_HELP`/`_USAGE`: + ``` + HELP: "Help text" + USAGE: "Usage text" + MANPAGE_SYNOPSYS: "Synopsys..." + MANPAGE_DESCRIPTION: (./description.txt) + ``` - Added `Path.lines()`. - Added `Text.find(text, target, start=1)`. - Added `at_cleanup()` to register cleanup functions. diff --git a/docs/command-line-parsing.md b/docs/command-line-parsing.md index 714e6e9c..92e9a1c9 100644 --- a/docs/command-line-parsing.md +++ b/docs/command-line-parsing.md @@ -223,3 +223,36 @@ OPTIONS --frob | --no-frob Whether or not to frob your gropnoggles ``` + +## Metadata + +You can specify metadata for a program, which is used for CLI messages like +`--help`, as well as manpage documentation. Metadata can be specified as either +a text literal (no interpolation) or as a file path literal. + +``` +USAGE: "--foo <n>" +HELP: " + This is some custom help text. + You can use these flags: + + --foo <n> The foo parameter + --help Show this message +" +MANPAGE_DESCRIPTION: (./description.roff) +``` + +Supported metadata: + +- `USAGE`: the short form usage shown in CLI parsing errors and help pages. This + should be a single line without the name of the program, so `USAGE: "--foo"` + would translate to the error message `Usage: myprogram --foo`. If this is not + present, it will be generated automatically. + +- `HELP`: The help message displayed when the `--help` flag is used or when there + is an argument parsing error. This should be a description of the program with + a multi-line documentation of commonly used flags. + +- `MANPAGE_SYNOPSYS`: the synopsis section of the manpage (inserted literally). + +- `MANPAGE_DESCRIPTION`: the description section of the manpage (inserted literally). @@ -274,6 +274,8 @@ Text_t ast_to_sexp(ast_t *ast) { T(Assert, "(Assert ", ast_to_sexp(data.expr), " ", optional_sexp("message", data.message), ")"); T(Use, "(Use ", optional_sexp("var", data.var), " ", quoted_text(data.path), ")"); T(InlineCCode, "(InlineCCode ", ast_list_to_sexp(data.chunks), optional_type_sexp("type", data.type_ast), ")"); + T(Metadata, "((Metadata ", Text$quoted(data.key, false, Text("\"")), " ", + Text$quoted(data.value, false, Text("\"")), ")"); default: errx(1, "S-expressions are not implemented for this AST"); #undef T } @@ -463,7 +465,8 @@ void ast_visit(ast_t *ast, void (*visitor)(ast_t *, void *), void *userdata) { case Int: case Num: case Path: - case TextLiteral: return; + case TextLiteral: + case Metadata: return; case TextJoin: ast_visit_list(Match(ast, TextJoin)->children, visitor, userdata); return; case Declare: { DeclareMatch(decl, ast, Declare); @@ -780,3 +783,15 @@ void type_ast_visit(ast_t *ast, void (*visitor)(type_ast_t *, void *), void *use Closure_t fn = {.fn = visitor, .userdata = userdata}; ast_visit(ast, _type_ast_visit, &fn); } + +OptionalText_t ast_metadata(ast_t *ast, const char *key) { + if (ast->tag != Block) return NONE_TEXT; + Text_t key_text = Text$from_str(key); + for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { + if (stmt->ast->tag == Metadata) { + DeclareMatch(m, stmt->ast, Metadata); + if (Text$equal_values(m->key, key_text)) return m->value; + } + } + return NONE_TEXT; +} @@ -276,6 +276,7 @@ typedef enum { Use, InlineCCode, ExplicitlyTyped, + Metadata, } ast_e; #define NUM_AST_TAGS (ExplicitlyTyped + 1) @@ -458,6 +459,9 @@ struct ast_s { ast_t *ast; struct type_s *type; } ExplicitlyTyped; + struct { + Text_t key, value; + } Metadata; } __data; }; @@ -483,3 +487,4 @@ CONSTFUNC ast_e binop_tag(ast_e tag); CONSTFUNC bool is_binary_operation(ast_t *ast); void ast_visit(ast_t *ast, void (*visitor)(ast_t *, void *), void *userdata); void type_ast_visit(ast_t *ast, void (*visitor)(type_ast_t *, void *), void *userdata); +OptionalText_t ast_metadata(ast_t *ast, const char *key); diff --git a/src/compile/cli.c b/src/compile/cli.c index e5756521..b92a5784 100644 --- a/src/compile/cli.c +++ b/src/compile/cli.c @@ -58,80 +58,86 @@ static OptionalText_t flagify(const char *name, bool prefix) { return flag; } -public -Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version) { +static Text_t generate_usage(env_t *env, type_t *fn_type) { DeclareMatch(fn_info, fn_type, FunctionType); - - env_t *main_env = fresh_scope(env); - - Text_t code = EMPTY_TEXT; - binding_t *usage_binding = get_binding(env, "_USAGE"); - Text_t usage_code = usage_binding ? usage_binding->code : Text("usage"); - binding_t *help_binding = get_binding(env, "_HELP"); - Text_t help_code = help_binding ? help_binding->code : usage_code; - if (!usage_binding) { - bool explicit_help_flag = false; - for (arg_t *arg = fn_info->args; arg; arg = arg->next) { - if (streq(arg->name, "help")) { - explicit_help_flag = true; - break; - } + bool explicit_help_flag = false; + for (arg_t *arg = fn_info->args; arg; arg = arg->next) { + if (streq(arg->name, "help")) { + explicit_help_flag = true; + break; } - - Text_t usage = explicit_help_flag ? EMPTY_TEXT : Text(" [\x1b[1m--help\x1b[m]"); - for (arg_t *arg = fn_info->args; arg; arg = arg->next) { - usage = Texts(usage, " "); - type_t *t = get_arg_type(main_env, arg); - OptionalText_t flag = flagify(arg->name, arg->default_val != NULL); - assert(flag.tag != TEXT_NONE); - OptionalText_t alias_flag = flagify(arg->alias, arg->default_val != NULL); - Text_t flags = Texts("\x1b[1m", flag, "\x1b[m"); - if (alias_flag.tag != TEXT_NONE) flags = Texts(flags, ",\x1b[1m", alias_flag, "\x1b[m"); + } + env_t *main_env = fresh_scope(env); + Text_t usage = explicit_help_flag ? EMPTY_TEXT : Text(" [\x1b[1m--help\x1b[m]"); + for (arg_t *arg = fn_info->args; arg; arg = arg->next) { + usage = Texts(usage, " "); + type_t *t = get_arg_type(main_env, arg); + OptionalText_t flag = flagify(arg->name, arg->default_val != NULL); + assert(flag.tag != TEXT_NONE); + OptionalText_t alias_flag = flagify(arg->alias, arg->default_val != NULL); + Text_t flags = Texts("\x1b[1m", flag, "\x1b[m"); + if (alias_flag.tag != TEXT_NONE) flags = Texts(flags, ",\x1b[1m", alias_flag, "\x1b[m"); + if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) + flags = Texts(flags, "|\x1b[1m--no-", Text$without_prefix(flag, Text("--")), "\x1b[m"); + if (arg->default_val || value_type(t)->tag == BoolType) { if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) - flags = Texts(flags, "|\x1b[1m--no-", Text$without_prefix(flag, Text("--")), "\x1b[m"); - if (arg->default_val || value_type(t)->tag == BoolType) { - if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) - usage = Texts(usage, "[", flags, "]"); - else if (t->tag == ListType) usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]"); - else usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]"); - } else { - usage = Texts(usage, "\x1b[1m", get_flag_options(t, Text("|")), "\x1b[m"); - } + usage = Texts(usage, "[", flags, "]"); + else if (t->tag == ListType) usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]"); + else usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]"); + } else { + usage = Texts(usage, "\x1b[1m", get_flag_options(t, Text("|")), "\x1b[m"); } - code = Texts(code, - "Text_t usage = Texts(Text(\"\\x1b[1mUsage:\\x1b[m \"), " - "Text$from_str(argv[0])", - usage.length == 0 ? EMPTY_TEXT : Texts(", Text(", quoted_text(usage), ")"), ");\n"); } - if (!help_binding) { - Text_t help_text = fn_info->args ? Text("\n") : Text("\n\n\x1b[2;3m No arguments...\x1b[m"); - - for (arg_t *arg = fn_info->args; arg; arg = arg->next) { - help_text = Texts(help_text, "\n"); - type_t *t = get_arg_type(main_env, arg); - OptionalText_t flag = flagify(arg->name, true); - assert(flag.tag != TEXT_NONE); - OptionalText_t alias_flag = flagify(arg->alias, true); - Text_t flags = Texts("\x1b[33;1m", flag, "\x1b[m"); - if (alias_flag.tag != TEXT_NONE) flags = Texts(flags, ",\x1b[33;1m", alias_flag, "\x1b[m"); - if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) - flags = Texts(flags, "|\x1b[33;1m--no-", Text$without_prefix(flag, Text("--")), "\x1b[m"); - if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) - help_text = Texts(help_text, " ", flags); - else - help_text = Texts(help_text, " ", flags, " \x1b[1;34m", - get_flag_options(t, Text("\x1b[m | \x1b[1;34m")), "\x1b[m"); - - if (arg->comment.length > 0) help_text = Texts(help_text, " \x1b[3m", arg->comment, "\x1b[m"); - if (arg->default_val) { - Text_t default_text = - Text$from_strn(arg->default_val->start, (size_t)(arg->default_val->end - arg->default_val->start)); - help_text = Texts(help_text, " \x1b[2m(default:", default_text, ")\x1b[m"); - } + return usage; +} + +static Text_t generate_help(env_t *env, type_t *fn_type) { + DeclareMatch(fn_info, fn_type, FunctionType); + env_t *main_env = fresh_scope(env); + Text_t help_text = EMPTY_TEXT; + + for (arg_t *arg = fn_info->args; arg; arg = arg->next) { + help_text = Texts(help_text, "\n"); + type_t *t = get_arg_type(main_env, arg); + OptionalText_t flag = flagify(arg->name, true); + assert(flag.tag != TEXT_NONE); + OptionalText_t alias_flag = flagify(arg->alias, true); + Text_t flags = Texts("\x1b[33;1m", flag, "\x1b[m"); + if (alias_flag.tag != TEXT_NONE) flags = Texts(flags, ",\x1b[33;1m", alias_flag, "\x1b[m"); + if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) + flags = Texts(flags, "|\x1b[33;1m--no-", Text$without_prefix(flag, Text("--")), "\x1b[m"); + if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) + help_text = Texts(help_text, " ", flags); + else + help_text = Texts(help_text, " ", flags, " \x1b[1;34m", get_flag_options(t, Text("\x1b[m | \x1b[1;34m")), + "\x1b[m"); + + if (arg->comment.length > 0) help_text = Texts(help_text, " \x1b[3m", arg->comment, "\x1b[m"); + if (arg->default_val) { + Text_t default_text = + Text$from_strn(arg->default_val->start, (size_t)(arg->default_val->end - arg->default_val->start)); + help_text = Texts(help_text, " \x1b[2m(default:", default_text, ")\x1b[m"); } - code = Texts(code, "Text_t help = Texts(usage, ", quoted_text(Texts(help_text, "\n")), ");\n"); - help_code = Text("help"); } + return help_text; +} + +public +Text_t compile_cli_arg_call(env_t *env, ast_t *ast, Text_t fn_name, type_t *fn_type, const char *version) { + DeclareMatch(fn_info, fn_type, FunctionType); + + Text_t code = EMPTY_TEXT; + OptionalText_t usage = ast_metadata(ast, "USAGE"); + if (usage.tag == TEXT_NONE) usage = generate_usage(env, fn_type); + + OptionalText_t help = ast_metadata(ast, "HELP"); + if (help.tag == TEXT_NONE) help = generate_help(env, fn_type); + + code = Texts(code, + "Text_t usage = Texts(Text(\"\\x1b[1mUsage:\\x1b[m \"), " + "Text$from_str(argv[0]), Text(\" \")", + usage.length == 0 ? EMPTY_TEXT : Texts(", Text(", quoted_text(usage), ")"), ");\n", + "Text_t help = Texts(usage, Text(\"\\n\\n\"", quoted_text(help), "));\n"); for (arg_t *arg = fn_info->args; arg; arg = arg->next) { code = Texts(code, compile_declaration(arg->type, Texts("_$", Text$from_str(arg->name))), " = ", @@ -151,7 +157,7 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c "},\n"); } code = Texts(code, "};\n"); - code = Texts(code, "tomo_parse_args(argc, argv, ", usage_code, ", ", help_code, ", ", version_code, + code = Texts(code, "tomo_parse_args(argc, argv, usage, help, ", version_code, ", sizeof(cli_args)/sizeof(cli_args[0]), cli_args);\n"); // Lazily initialize default values to prevent side effects diff --git a/src/compile/cli.h b/src/compile/cli.h index fa60eccf..fbb7347b 100644 --- a/src/compile/cli.h +++ b/src/compile/cli.h @@ -2,9 +2,10 @@ #pragma once +#include "../ast.h" #include "../environment.h" #include "../stdlib/datatypes.h" #include "../types.h" -Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version); +Text_t compile_cli_arg_call(env_t *env, ast_t *ast, Text_t fn_name, type_t *fn_type, const char *version); Text_t compile_manpage(Text_t program, OptionalText_t synopsis, OptionalText_t description, arg_t *args); diff --git a/src/compile/files.c b/src/compile/files.c index a4cc07fe..b916f23f 100644 --- a/src/compile/files.c +++ b/src/compile/files.c @@ -142,6 +142,7 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) { } return code; } + case Metadata: default: return EMPTY_TEXT; } } diff --git a/src/compile/statements.c b/src/compile/statements.c index 0cd85b5d..c6ceccd9 100644 --- a/src/compile/statements.c +++ b/src/compile/statements.c @@ -216,6 +216,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) { return EMPTY_TEXT; } } + case Metadata: return EMPTY_TEXT; default: // print("Is discardable: ", ast_to_sexp_str(ast), " ==> ", // is_discardable(env, ast)); diff --git a/src/parse/expressions.c b/src/parse/expressions.c index b43e4f3a..d031c49f 100644 --- a/src/parse/expressions.c +++ b/src/parse/expressions.c @@ -168,7 +168,7 @@ ast_t *parse_term_no_suffix(parse_ctx_t *ctx, const char *pos) { (void)(false || (term = parse_none(ctx, pos)) || (term = parse_num(ctx, pos)) // Must come before int || (term = parse_int(ctx, pos)) || (term = parse_negative(ctx, pos)) // Must come after num/int || (term = parse_heap_alloc(ctx, pos)) || (term = parse_stack_reference(ctx, pos)) - || (term = parse_bool(ctx, pos)) || (term = parse_text(ctx, pos)) || (term = parse_path(ctx, pos)) + || (term = parse_bool(ctx, pos)) || (term = parse_text(ctx, pos, true)) || (term = parse_path(ctx, pos)) || (term = parse_lambda(ctx, pos)) || (term = parse_parens(ctx, pos)) || (term = parse_table(ctx, pos)) || (term = parse_var(ctx, pos)) || (term = parse_list(ctx, pos)) || (term = parse_reduction(ctx, pos)) || (term = parse_pass(ctx, pos)) || (term = parse_defer(ctx, pos)) || (term = parse_skip(ctx, pos)) diff --git a/src/parse/files.c b/src/parse/files.c index 23e940e9..f5d9554a 100644 --- a/src/parse/files.c +++ b/src/parse/files.c @@ -7,8 +7,11 @@ #include <string.h> #include "../ast.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/paths.h" #include "../stdlib/stdlib.h" #include "../stdlib/tables.h" +#include "../stdlib/text.h" #include "../stdlib/util.h" #include "context.h" #include "errors.h" @@ -31,6 +34,35 @@ static ast_t *parse_top_declaration(parse_ctx_t *ctx, const char *pos) { return declaration; } +static ast_t *parse_metadata(parse_ctx_t *ctx, const char *pos) { + const char *start = pos; + const char *key = get_id(&pos); + if (!key) return NULL; + spaces(&pos); + if (!match(&pos, ":")) return NULL; + spaces(&pos); + ast_t *value = parse_text(ctx, pos, false); + Text_t value_text = EMPTY_TEXT; + if (value) { + for (ast_list_t *child = Match(value, TextJoin)->children; child; child = child->next) { + if (child->ast->tag != TextLiteral) + parser_err(ctx, child->ast->start, child->ast->end, "Text interpolations are not allowed in metadata"); + value_text = Texts(value_text, Match(child->ast, TextLiteral)->text); + } + } else { + value = parse_path(ctx, pos); + if (!value) return NULL; + Path_t path = Path$from_str(Match(value, Path)->path); + path = Path$resolved(path, Path$parent(Path$from_str(ctx->file->filename))); + OptionalText_t contents = Path$read(path); + if (contents.tag == TEXT_NONE) parser_err(ctx, value->start, value->end, "File not found: ", path); + value_text = Text$trim(contents, Text("\r\n\t "), true, true); + } + pos = value->end; + + return NewAST(ctx->file, start, pos, Metadata, .key = Text$from_str(key), .value = value_text); +} + ast_t *parse_file_body(parse_ctx_t *ctx, const char *pos) { const char *start = pos; whitespace(ctx, &pos); @@ -40,10 +72,11 @@ ast_t *parse_file_body(parse_ctx_t *ctx, const char *pos) { whitespace(ctx, &next); if (get_indent(ctx, next) != 0) break; ast_t *stmt; - if ((stmt = optional(ctx, &pos, parse_struct_def)) || (stmt = optional(ctx, &pos, parse_func_def)) - || (stmt = optional(ctx, &pos, parse_enum_def)) || (stmt = optional(ctx, &pos, parse_lang_def)) - || (stmt = optional(ctx, &pos, parse_convert_def)) || (stmt = optional(ctx, &pos, parse_use)) - || (stmt = optional(ctx, &pos, parse_inline_c)) || (stmt = optional(ctx, &pos, parse_top_declaration))) { + if ((stmt = optional(ctx, &pos, parse_metadata)) || (stmt = optional(ctx, &pos, parse_struct_def)) + || (stmt = optional(ctx, &pos, parse_func_def)) || (stmt = optional(ctx, &pos, parse_enum_def)) + || (stmt = optional(ctx, &pos, parse_lang_def)) || (stmt = optional(ctx, &pos, parse_convert_def)) + || (stmt = optional(ctx, &pos, parse_use)) || (stmt = optional(ctx, &pos, parse_inline_c)) + || (stmt = optional(ctx, &pos, parse_top_declaration))) { statements = new (ast_list_t, .ast = stmt, .next = statements); pos = stmt->end; whitespace(ctx, &pos); // TODO: check for newline diff --git a/src/parse/text.c b/src/parse/text.c index 7650955c..e23b8417 100644 --- a/src/parse/text.c +++ b/src/parse/text.c @@ -17,7 +17,7 @@ #include "types.h" #include "utils.h" -static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos) { +static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, bool allow_interps) { const char *pos = *out_pos; int64_t starting_indent = get_indent(ctx, pos); @@ -41,6 +41,8 @@ static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos) { parser_err(ctx, pos, pos, "I expected a valid text here"); } + if (!allow_interps) interp = NULL; + ast_list_t *chunks = NULL; Text_t chunk = EMPTY_TEXT; const char *chunk_start = pos; @@ -123,9 +125,9 @@ static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos) { return chunks; } -ast_t *parse_text(parse_ctx_t *ctx, const char *pos) { +ast_t *parse_text(parse_ctx_t *ctx, const char *pos, bool allow_interps) { // ('"' ... '"' / "'" ... "'" / "`" ... "`") - // "$" [name] [interp-char] quote-char ... close-quote + // "$" [name] quote-char ... close-quote const char *start = pos; const char *lang = NULL; @@ -136,7 +138,7 @@ ast_t *parse_text(parse_ctx_t *ctx, const char *pos) { if (!(*pos == '"' || *pos == '\'' || *pos == '`')) return NULL; - ast_list_t *chunks = _parse_text_helper(ctx, &pos); + ast_list_t *chunks = _parse_text_helper(ctx, &pos, allow_interps); bool colorize = match(&pos, "~") && match_word(&pos, "colorized"); return NewAST(ctx->file, start, pos, TextJoin, .lang = lang, .children = chunks, .colorize = colorize); } @@ -157,7 +159,7 @@ ast_t *parse_inline_c(parse_ctx_t *ctx, const char *pos) { parser_err(ctx, pos, pos + 1, "This is not a valid string quotation character. Valid characters are: \"'`|/;([{<"); - ast_list_t *chunks = _parse_text_helper(ctx, &pos); + ast_list_t *chunks = _parse_text_helper(ctx, &pos, true); return NewAST(ctx->file, start, pos, InlineCCode, .chunks = chunks, .type_ast = type); } diff --git a/src/parse/text.h b/src/parse/text.h index 6ab3cab2..865bc5a4 100644 --- a/src/parse/text.h +++ b/src/parse/text.h @@ -1,9 +1,11 @@ // Logic for parsing text literals #pragma once +#include <stdbool.h> + #include "../ast.h" #include "context.h" -ast_t *parse_text(parse_ctx_t *ctx, const char *pos); +ast_t *parse_text(parse_ctx_t *ctx, const char *pos, bool allow_interps); ast_t *parse_inline_c(parse_ctx_t *ctx, const char *pos); ast_t *parse_path(parse_ctx_t *ctx, const char *pos); @@ -841,7 +841,7 @@ void transpile_code(env_t *base_env, Path_t path) { namespace_name(module_env, module_env->namespace, Text("$initialize")), "();\n" "\n", - compile_cli_arg_call(module_env, main_binding->code, main_binding->type, version), + compile_cli_arg_call(module_env, ast, main_binding->code, main_binding->type, version), "return 0;\n" "}\n")); } @@ -877,7 +877,9 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t Path_t manpage_file = build_file(Path$with_extension(path, Text(".1"), true), ""); if (clean_build || !Path$is_file(manpage_file, true) || is_stale(manpage_file, path, true)) { - Text_t manpage = compile_manpage(Path$base_name(exe_path), NONE_TEXT, NONE_TEXT, + OptionalText_t synopsys = ast_metadata(ast, "MANPAGE_SYNOPSYS"); + OptionalText_t description = ast_metadata(ast, "MANPAGE_DESCRIPTION"); + Text_t manpage = compile_manpage(Path$base_name(exe_path), synopsys, description, Match(main_binding->type, FunctionType)->args); if (!quiet) print("Wrote manpage:\t", Path$relative_to(manpage_file, Path$current_dir())); Path$write(manpage_file, manpage, 0644); diff --git a/src/typecheck.c b/src/typecheck.c index 98fbf6da..4e1f5554 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -1511,6 +1511,7 @@ type_t *get_type(env_t *env, ast_t *ast) { } case Unknown: code_err(ast, "I can't figure out the type of: ", ast_to_sexp_str(ast)); case ExplicitlyTyped: return Match(ast, ExplicitlyTyped)->type; + case Metadata: return Type(VoidType); } #ifdef __GNUC__ #pragma GCC diagnostic pop @@ -1529,7 +1530,8 @@ PUREFUNC bool is_discardable(env_t *env, ast_t *ast) { case StructDef: case EnumDef: case LangDef: - case Use: return true; + case Use: + case Metadata: return true; default: break; } type_t *t = get_type(env, ast); @@ -1686,7 +1688,8 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) { default: return is_constant(env, binop.lhs) && is_constant(env, binop.rhs); } } - case Use: return true; + case Use: + case Metadata: return true; case FunctionCall: return false; case InlineCCode: return true; default: return false; |
