llm
Download
117 lines 5.7 KiBCModified 2026-08-31 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
// Command line argument parsing

#pragma once

#include <stdbool.h>

#include "datatypes/typeinfo.h"

typedef struct {
    const char *name;
    void *dest;
    const TypeInfo_t *type;
    char short_flag;
    bool required, populated;
    // Optional presentation info for autogenerated usage/help text:
    const char *description; // one-line description of the flag
    const char *metavar; // display name for the value (default: derived from the type)
    const char *default_text; // the default value, as source text, for help output
    bool positional; // display as a positional argument instead of a flag
} cli_arg_t;

// The help/version/usage text for one argument-parsing pass, along with which
// short flags (if any) the automatic --help and --version flags claim:
typedef struct {
    Text_t usage, help;
    char help_short; // short alias for --help ('h'), or 0 for none
    bool strict_positionals; // only fill positional values into .positional spec entries;
                             // otherwise any unpopulated entry can be filled positionally
    // Appended to parse errors: when the first word resembles one of this
    // command's subcommand names, a failed parse is much more likely a
    // mistyped command than a genuinely bad argument. Only shown on failure,
    // so a word that resembles a command but really is an argument (a
    // directory named `tests`, say) still runs normally.
    Text_t command_hint;
} cli_help_info_t;

void tomo_parse_arg_list(List_t args, cli_help_info_t info, int spec_len, cli_arg_t spec[spec_len]);

// Fills in a command's `usage` when the program doesn't supply one of its own.
Text_t tomo_generate_usage(Text_t prefix, int spec_len, cli_arg_t spec[spec_len]);

// One command in a git-style CLI. Commands nest: `tomo build`, `git submodule
// init`. A command with children can still have a handler of its own (like
// `git stash` alongside `git stash pop`), and a command with no handler is
// purely a namespace for its children.
typedef struct cli_command_s cli_command_t;
struct cli_command_s {
    const char *name; // "run", "build", ... (NULL for the root command)
    // An alternate name that invokes the same command ("fmt" for "format"),
    // or NULL. Accepted wherever the name is, but not listed as a command of
    // its own:
    const char *alias;
    const char *summary; // one-liner shown in the parent's command listing
    const char *description; // optional prose paragraph for this command's help
    // Usage/help shown for `<prog> <cmd> --help` and parse errors; leave empty
    // to have them autogenerated from the spec and children at startup:
    Text_t usage, help;
    int spec_len;
    cli_arg_t *spec; // this command's own flags and positionals
    int num_children;
    cli_command_t **children; // nested subcommands, or NULL
    // Runs after this command's spec is parsed. extra_args is the raw argv
    // tail after the first bare "--", untouched. NULL means this command is
    // only a namespace: naming it without a child prints its help.
    int (*handler)(cli_command_t *self, List_t extra_args);
};

// A whole command-line program. Global flags are valid anywhere on the command
// line (they are popped before dispatch), so global and command-specific flag
// names/short flags must be disjoint.
typedef struct {
    const char *name; // the program name, e.g. "tomo"
    const char *summary; // one-liner shown at the top of the help
    const char *description; // optional prose paragraph for the top-level help
    const char *version; // NULL: no automatic --version flag
    char version_short; // short alias for --version ('v'), or 0 for long-only
    int global_len;
    cli_arg_t *global_spec; // flags valid anywhere on the command line
    // Runs after global flags are popped, before dispatch:
    void (*after_globals)(void);
    // Only fill positional values into args marked .positional. Programs whose
    // args are generated (rather than hand-written) leave this false, so every
    // argument can be given either positionally or by flag.
    bool strict_positionals;
    // Treat everything after the first bare "--" as opaque arguments to hand
    // to the command's handler (`tomo run prog.tm -- prog args...`). Programs
    // that don't relay arguments elsewhere leave this false, and then "--"
    // keeps its usual meaning of "the rest are values, not flags", which is
    // how a value that starts with a dash (or collides with a command name)
    // gets passed.
    bool passthrough_after_double_dash;
    // The root command: its children are the program's top-level commands, and
    // its own spec/handler (if any) run when the first word doesn't name one --
    // e.g. `tomo file.tm` and a Tomo program's own `main()`.
    cli_command_t root;
} cli_spec_t;

int tomo_dispatch_command(int argc, char *argv[], cli_spec_t *cli);

// The palette for help, usage, and error text, with every escape emptied out when
// output isn't colored (NO_COLOR, COLOR=0, or not a terminal). Same approach as
// report_style() in report.h: each site writes `style.bold` unconditionally
// rather than branching on USE_COLOR or filtering escapes back out afterwards.
typedef struct {
    const char *bold, *dim, *italic, *reset;
    const char *heading; // section headings: "Flags:", "Commands:"
    const char *usage; // the "Usage:" label
    const char *flag; // --flag names
    const char *value; // value placeholders: text, N, path
    const char *command; // subcommand names
    const char *error; // error messages
} cli_style_t;

cli_style_t tomo_cli_style(void);

bool pop_cli_flag(List_t *args, char short_flag, const char *flag, void *dest, const TypeInfo_t *type);
bool pop_cli_positional(List_t *args, const char *flag, void *dest, const TypeInfo_t *type, bool allow_dashes);