{"id":13993106,"url":"https://github.com/leanprover/lean4-cli","last_synced_at":"2025-10-31T15:30:41.760Z","repository":{"id":37009525,"uuid":"341363356","full_name":"leanprover/lean4-cli","owner":"leanprover","description":"A Lean 4 library for configuring Command Line Interfaces and parsing command line arguments.","archived":false,"fork":false,"pushed_at":"2025-02-03T13:38:18.000Z","size":112,"stargazers_count":73,"open_issues_count":1,"forks_count":16,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-02-03T14:37:51.756Z","etag":null,"topics":["cli","lean","lean4"],"latest_commit_sha":null,"homepage":"","language":"Lean","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leanprover.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-22T23:07:25.000Z","updated_at":"2025-02-03T13:36:50.000Z","dependencies_parsed_at":"2024-04-25T13:55:56.196Z","dependency_job_id":"95970169-abda-4ce1-b510-6c44f31ca21f","html_url":"https://github.com/leanprover/lean4-cli","commit_stats":null,"previous_names":["leanprover/lean4-cli","mhuisi/lean4-cli"],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leanprover%2Flean4-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leanprover%2Flean4-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leanprover%2Flean4-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leanprover%2Flean4-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leanprover","download_url":"https://codeload.github.com/leanprover/lean4-cli/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239212561,"owners_count":19600830,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","lean","lean4"],"created_at":"2024-08-09T14:02:14.030Z","updated_at":"2025-10-31T15:30:41.713Z","avatar_url":"https://github.com/leanprover.png","language":"Lean","funding_links":[],"categories":["Lean"],"sub_categories":[],"readme":"# lean4-cli\nThis project is maintained by [@mhuisi](https://github.com/mhuisi).\n\n## Usage\nSee [the documentation of Lake](https://github.com/leanprover/lean4/blob/master/src/lake/README.md).\n\n### Configuration\nCommands are configured with a lightweight DSL. The following declarations define a command `exampleCmd` with two subcommands `installCmd` and `testCmd`. `runExampleCmd` denotes a handler that is called when the command is run and is described further down below in the **Command Handlers** subsection.\n\n```Lean\nopen Cli\n\ndef installCmd := `[Cli|\n  installCmd NOOP;\n  \"installCmd provides an example for a subcommand without flags or arguments that does nothing. \" ++\n  \"Versions can be omitted.\"\n]\n\ndef testCmd := `[Cli|\n  testCmd NOOP;\n  \"testCmd provides another example for a subcommand without flags or arguments that does nothing.\"\n]\n\ndef exampleCmd : Cmd := `[Cli|\n  exampleCmd VIA runExampleCmd; [\"0.0.1\"]\n  \"This string denotes the description of `exampleCmd`.\"\n\n  FLAGS:\n    verbose;                    \"Declares a flag `--verbose`. This is the description of the flag.\"\n    i, invert;                  \"Declares a flag `--invert` with an associated short alias `-i`.\"\n    o, optimize;                \"Declares a flag `--optimize` with an associated short alias `-o`.\"\n    p, priority : Nat;          \"Declares a flag `--priority` with an associated short alias `-p` \" ++\n                                \"that takes an argument of type `Nat`.\"\n    module : ModuleName;        \"Declares a flag `--module` that takes an argument of type `ModuleName` \" ++\n                                \"which be can used to reference Lean modules like `Init.Data.Array` \" ++\n                                \"or Lean files using a relative path like `Init/Data/Array.lean`.\"\n    \"set-paths\" : Array String; \"Declares a flag `--set-paths` \" ++\n                                \"that takes an argument of type `Array Nat`. \" ++\n                                \"Quotation marks allow the use of hyphens.\"\n\n  ARGS:\n    input : String;      \"Declares a positional argument \u003cinput\u003e \" ++\n                         \"that takes an argument of type `String`.\"\n    ...outputs : String; \"Declares a variable argument \u003coutput\u003e... \" ++\n                         \"that takes an arbitrary amount of arguments of type `String`.\"\n\n  SUBCOMMANDS:\n    installCmd;\n    testCmd\n\n  -- The EXTENSIONS section denotes features that\n  -- were added as an external extension to the library.\n  -- `./Cli/Extensions.lean` provides some commonly useful examples.\n  EXTENSIONS:\n    author \"mhuisi\";\n    defaultValues! #[(\"priority\", \"0\")]\n]\n```\n\n### Command handlers\nThe command handler `runExampleCmd` demonstrates how to use the parsed user input.\n\n```Lean\ndef runExampleCmd (p : Parsed) : IO UInt32 := do\n  let input   : String       := p.positionalArg! \"input\" |\u003e.as! String\n  let outputs : Array String := p.variableArgsAs! String\n  IO.println \u003c| \"Input: \" ++ input\n  IO.println \u003c| \"Outputs: \" ++ toString outputs\n\n  if p.hasFlag \"verbose\" then\n    IO.println \"Flag `--verbose` was set.\"\n  if p.hasFlag \"invert\" then\n    IO.println \"Flag `--invert` was set.\"\n  if p.hasFlag \"optimize\" then\n    IO.println \"Flag `--optimize` was set.\"\n\n  let priority : Nat := p.flag! \"priority\" |\u003e.as! Nat\n  IO.println \u003c| \"Flag `--priority` always has at least a default value: \" ++ toString priority\n\n  if p.hasFlag \"module\" then\n    let moduleName : ModuleName := p.flag! \"module\" |\u003e.as! ModuleName\n    IO.println \u003c| s!\"Flag `--module` was set to `{moduleName}`.\"\n\n  if let some setPathsFlag := p.flag? \"set-paths\" then\n    IO.println \u003c| toString \u003c| setPathsFlag.as! (Array String)\n  return 0\n```\n\n### Running the command\nBelow you can find some simple examples of how to pass user input to the Cli library.\n\n```lean\ndef main (args : List String) : IO UInt32 :=\n  exampleCmd.validate args\n\n#eval main \u003c| \"-i -o -p 1 --module=Lean.Compiler --set-paths=path1,path2,path3 input output1 output2\".splitOn \" \"\n/-\nYields:\n  Input: input\n  Outputs: #[output1, output2]\n  Flag `--invert` was set.\n  Flag `--optimize` was set.\n  Flag `--priority` always has at least a default value: 1\n  Flag `--module` was set to `Lean.Compiler`.\n  #[path1, path2, path3]\n-/\n\n-- Short parameterless flags can be grouped,\n-- short flags with parameters do not need to be separated from\n-- the corresponding value.\n#eval main \u003c| \"-io -p1 input\".splitOn \" \"\n/-\nYields:\n  Input: input\n  Outputs: #[]\n  Flag `--invert` was set.\n  Flag `--optimize` was set.\n  Flag `--priority` always has at least a default value: 1\n-/\n\n#eval main \u003c| \"--version\".splitOn \" \"\n/-\nYields:\n  0.0.1\n-/\n```\n\n\n### Help\nUpon calling `-h`, the above configuration produces the following help.\n```\nexampleCmd [0.0.1]\nmhuisi\nThis string denotes the description of `exampleCmd`.\n\nUSAGE:\n    exampleCmd [SUBCOMMAND] [FLAGS] \u003cinput\u003e \u003coutputs\u003e...\n\nFLAGS:\n    -h, --help                  Prints this message.\n    --version                   Prints the version.\n    --verbose                   Declares a flag `--verbose`. This is the\n                                description of the flag.\n    -i, --invert                Declares a flag `--invert` with an associated\n                                short alias `-i`.\n    -o, --optimize              Declares a flag `--optimize` with an associated\n                                short alias `-o`.\n    -p, --priority : Nat        Declares a flag `--priority` with an associated\n                                short alias `-p` that takes an argument of type\n                                `Nat`. [Default: `0`]\n    --module : ModuleName       Declares a flag `--module` that takes an\n                                argument of type `ModuleName` which can be used\n                                to reference Lean modules like `Init.Data.Array`\n                                or Lean files using a relative path like\n                                `Init/Data/Array.lean`.\n    --set-paths : Array String  Declares a flag `--set-paths` that takes an\n                                argument of type `Array Nat`. Quotation marks\n                                allow the use of hyphens.\n\nARGS:\n    input : String    Declares a positional argument \u003cinput\u003e that takes an\n                      argument of type `String`.\n    outputs : String  Declares a variable argument \u003coutput\u003e... that takes an\n                      arbitrary amount of arguments of type `String`.\n\nSUBCOMMANDS:\n    installCmd  installCmd provides an example for a subcommand without flags or\n                arguments that does nothing. Versions can be omitted.\n    testCmd     testCmd provides another example for a subcommand without flags\n                or arguments that does nothing.\n```\n\nThe full example can be found under `./Cli/Example.lean`.\n\n## Ad Hoc Documentation\nThis section documents only the most common features of the library. For the full documentation, peek into `./Cli/Basic.lean` and `./Cli/Extensions.lean`! All definitions below live in the `Cli` namespace.\n\n```Lean\n-- In a literalIdent, identifiers are expanded as `String`s.\nsyntax literalIdent := term\n\nsyntax runFun := (\" VIA \" term) \u003c|\u003e \" NOOP\"\n\nsyntax positionalArg := colGe literalIdent \" : \" term \"; \" term\n\nsyntax variableArg := colGe \"...\" literalIdent \" : \" term \"; \" term\n\nsyntax flag := colGe literalIdent (\",\" literalIdent)? (\" : \" term)? \"; \" term\n\nsyntax \"`[Cli|\\n\"\n    literalIdent runFun \"; \" (\"[\" term \"]\")?\n    term\n    (\"FLAGS:\\n\" withPosition((flag)*))?\n    (\"ARGS:\\n\" withPosition((positionalArg)* (variableArg)?))?\n    (\"SUBCOMMANDS: \" sepBy(term, \";\", \"; \"))?\n    (\"EXTENSIONS: \" sepBy(term, \";\", \"; \"))?\n  \"\\n]\" : term\n```\n\n```Lean\n/--\nValidates `args` by `Cmd.process?`ing the input according to `c`.\nNote that `args` designates the list `\u003cfoo\u003e` in `somebinary \u003cfoo\u003e`.\nPrints the help or the version of the called (sub)command if the respective flag was passed and\nreturns `0` for the exit code.\nIf neither of these flags were passed and processing was successful, the `run` handler of the\ncalled command is executed.\nIn the case of a processing error, the error is printed to stderr and an exit code of `1` is returned.\n-/\ndef validate (c : Cmd) (args : List String) : IO UInt32 := do\n  let result := c.process args\n  match result with\n  | .ok (cmd, parsed) =\u003e\n    if parsed.hasFlag \"help\" then\n      parsed.printHelp\n      return 0\n    if parsed.cmd.meta.hasVersion ∧ parsed.hasFlag \"version\" then\n      parsed.printVersion!\n      return 0\n    cmd.run parsed\n  | .error (cmd, err) =\u003e\n    cmd.printError err\n    return 1\n```\n```Lean\n  /-- Represents parsed user input data. -/\n  structure Parsed where\n    /-- Recursive meta-data of the associated command. -/\n    cmd            : Parsed.Cmd\n    /-- Parent of the associated command. -/\n    parent?        : Option Parsed.Cmd\n    /-- Parsed flags. -/\n    flags          : Array Parsed.Flag\n    /-- Parsed positional arguments. -/\n    positionalArgs : Array Parsed.Arg\n    /-- Parsed variable arguments. -/\n    variableArgs   : Array Parsed.Arg\n    deriving Inhabited\n\nnamespace Parsed\n  /-- Parent of the associated command. -/\n  def parent! (p : Parsed) : Parsed.Cmd\n\n  /-- Checks whether the associated command has a parent, i.e. whether it is not the root command. -/\n  def hasParent (p : Parsed) : Bool\n\n  /-- Finds the parsed flag in `p` with the corresponding `longName`. -/\n  def flag?          (p : Parsed) (longName : String) : Option Flag\n  /-- Finds the parsed positional argument in `p` with the corresponding `name`. -/\n  def positionalArg? (p : Parsed) (name : String)     : Option Arg\n\n  /-- Finds the parsed flag in `p` with the corresponding `longName`. -/\n  def flag!          (p : Parsed) (longName : String) : Flag\n  /-- Finds the parsed positional argument in `p` with the corresponding `name`. -/\n  def positionalArg! (p : Parsed) (name : String)     : Arg\n\n  /-- Checks whether `p` has a parsed flag with the corresponding `longName`. -/\n  def hasFlag          (p : Parsed) (longName : String) : Bool\n  /-- Checks whether `p` has a positional argument with the corresponding `longName`. -/\n  def hasPositionalArg (p : Parsed) (name : String)     : Bool\n\n  /--\n  Converts all `p.variableArgs` values to `τ`, which should be the same type\n  that was designated in the corresponding `Cli.Arg`.\n  Yields `none` if the conversion was unsuccessful, which can only\n  happen if `τ` is not the same type as the one designated in the corresponding `Cli.Arg`.\n  -/\n  def variableArgsAs? (p : Parsed) (τ) [ParseableType τ] : Option (Array τ)\n\n  /--\n  Converts all `p.variableArgs` values to `τ`, which should be the same type\n  that was designated in the corresponding `Cli.Arg`.\n  Panics if the conversion was unsuccessful, which can only\n  happen if `τ` is not the same type as the one designated in the corresponding `Cli.Arg`.\n  -/\n  def variableArgsAs! (p : Parsed) (τ) [Inhabited τ] [ParseableType τ] : Array τ\nend Parsed\n```\n```Lean\nnamespace Parsed\n  /--\n  Represents a flag and its parsed value.\n  Use `Parsed.Flag.as!` to convert the value to some `ParseableType`.\n  -/\n  structure Flag where\n    /-- Associated flag meta-data. -/\n    flag  : Flag\n    /-- Parsed value that was validated and conforms to `flag.type`. -/\n    value : String\n\n  namespace Flag\n    /--\n    Converts `f.value` to `τ`, which should be the same type\n    that was designated in `f.flag.type`.\n    Yields `none` if the conversion was unsuccessful, which can only\n    happen if `τ` is not the same type as the one designated in `f.flag.type`.\n    -/\n    def as? (f : Flag) (τ) [ParseableType τ] : Option τ\n    /--\n    Converts `f.value` to `τ`, which should be the same type\n    that was designated in `f.flag.type`.\n    Panics if the conversion was unsuccessful, which can only\n    happen if `τ` is not the same type as the one designated in `f.flag.type`.\n    -/\n    def as! (f : Flag) (τ) [Inhabited τ] [ParseableType τ] : τ\n  end Flag\n\n  /--\n  Represents an argument and its parsed value.\n  Use `Parsed.Arg.as!` to convert the value to some `ParseableType`.\n  -/\n  structure Arg where\n    /-- Associated argument meta-data. -/\n    arg   : Arg\n    /-- Parsed value that was validated and conforms to `arg.type`. -/\n    value : String\n\n  namespace Arg\n    /--\n    Converts `a.value` to `τ`, which should be the same type\n    that was designated in `a.arg.type`.\n    Yields `none` if the conversion was unsuccessful, which can only\n    happen if `τ` is not the same type as the one designated in `a.arg.type`.\n    -/\n    def as? (a : Arg) (τ) [ParseableType τ] : Option τ\n    /--\n    Converts `a.value` to `τ`, which should be the same type\n    that was designated in `a.arg.type`.\n    Panics if the conversion was unsuccessful, which can only\n    happen if `τ` is not the same type as the one designated in `a.arg.type`.\n    -/\n    def as! (a : Arg) (τ) [Inhabited τ] [ParseableType τ] : τ\n  end Arg\nend Parsed\n```\n```Lean\n/--\nCreates a new command. Adds a `-h, --help` and a `--version` flag if a version is designated.\nUpdates the `parentNames` of all subcommands.\n- `name`:                Name that is displayed in the help.\n- `version?`:            Version that is displayed in the help and when the version is queried.\n- `description`:         Description that is displayed in the help.\n- `furtherInformation?`: Information appended to the end of the help. Useful for command extensions.\n- `flags`:               Supported flags (\"options\" in standard terminology).\n- `positionalArgs`:      Supported positional arguments (\"operands\" in standard terminology).\n- `variableArg?`:        Variable argument at the end of the positional arguments.\n- `run`:                 Handler to run when the command is called and flags/arguments have been successfully processed.\n- `subCmds`:             Subcommands.\n- `extension?`:          Extension of the Cli library.\n-/\ndef mk\n  (name                : String)\n  (version?            : Option String)\n  (description         : String)\n  (furtherInformation? : Option String := none)\n  (flags               : Array Flag    := #[])\n  (positionalArgs      : Array Arg     := #[])\n  (variableArg?        : Option Arg    := none)\n  (run                 : Parsed → IO UInt32)\n  (subCmds             : Array Cmd        := #[])\n  (extension?          : Option Extension := none)\n  : Cmd\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleanprover%2Flean4-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleanprover%2Flean4-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleanprover%2Flean4-cli/lists"}