├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── argparse.nimble ├── builddocs.sh ├── changes ├── README.md └── config.toml ├── docs ├── argparse.html ├── argparse.idx ├── argparse │ ├── backend.html │ ├── backend.idx │ ├── filler.html │ ├── filler.idx │ ├── macrohelp.html │ ├── macrohelp.idx │ └── nimdoc.out.css ├── dochack.js ├── nimdoc.out.css └── theindex.html ├── src ├── .gitignore ├── argparse.nim └── argparse │ ├── backend.nim │ ├── filler.nim │ └── macrohelp.nim └── tests ├── .gitignore ├── config.nims ├── test1.nim ├── testBackend.nim └── testFiller.nim /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | schedule: 7 | - cron: '0 0 * * 1' 8 | 9 | jobs: 10 | tests: 11 | runs-on: ${{ matrix.os }} 12 | env: 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | strategy: 15 | matrix: 16 | nimversion: 17 | - choosenim:stable 18 | # - devel 19 | # no devel for now because GitHub Actions won't let me not fail for them 20 | - binary:1.6 21 | - binary:1.4 22 | - binary:1.2 23 | - binary:1.0 24 | os: 25 | - ubuntu-latest 26 | - macOS-13 27 | - windows-latest 28 | steps: 29 | - uses: actions/checkout@v1 30 | - uses: iffy/install-nim@v4 31 | with: 32 | version: ${{ matrix.nimversion }} 33 | - run: nim --version 34 | - name: Test 35 | run: | 36 | nimble test 37 | nimble refresh 38 | - name: Docs 39 | if: runner.os == 'Linux' && matrix.nimversion == 'stable' 40 | run: | 41 | ./builddocs.sh 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore extensionless files 2 | * 3 | !/**/ 4 | !*.* 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v4.0.2 - 2024-08-16 2 | 3 | - **FIX:** Internal: add missing cast to `{.nimcall.}` to move beyond things deprecated since Nim 0.18 4 | 5 | # v4.0.1 - 2022-11-28 6 | 7 | - **FIX:** Changed an internal invocation of `do:` to `do ():` to satisfy [RFC 486](https://github.com/nim-lang/RFCs/issues/486) 8 | 9 | # v4.0.0 - 2022-11-17 10 | 11 | - **BREAKING CHANGE:** You can now use hyphens in both `command("...")` and `arg("...")` names. ([#83](https://github.com/iffy/nim-argparse/issues/83)) 12 | 13 | # v3.0.1 - 2022-11-11 14 | 15 | - **FIX:** Added a `.help` field to `ShortCircuit` errors so that you can get subcommand help within an exception handler ([#62](https://github.com/iffy/nim-argparse/issues/62)) 16 | 17 | # v3.0.0 - 2022-01-31 18 | 19 | - **BREAKING CHANGE:** No longer export `macros` when importing `argparse`. Fixes [#73](https://github.com/iffy/nim-argparse/issues/73) 20 | 21 | # v2.1.0 - 2022-01-27 22 | 23 | - **NEW:** Parse result objects will now have a `OPTION_opt` attribute that is an `Option[string]`. This can be used to inspect if the option was actually given rather than blank. ([#68](https://github.com/iffy/nim-argparse/issues/68)) 24 | - **FIX:** When `required=true` for an option, passing a value via environment variable now fulfills the requirement. ([#67](https://github.com/iffy/nim-argparse/issues/67)) 25 | - **FIX:** Fix deprecation warning: use `delete(s, first..last)`; delete is deprecated ([#69](https://github.com/iffy/nim-argparse/issues/69)) 26 | 27 | # v2.0.1 - 2021-07-13 28 | 29 | - **FIX:** Removed program name from default subcommand help text 30 | - **FIX:** Restore ability to call `parse()` without args ([#64](https://github.com/iffy/nim-argparse/issues/64)) 31 | 32 | # v2.0.0 - 2020-12-03 33 | 34 | - **BREAKING CHANGE:** Calling `parse()` with `-h`/`--help` flags will now raise a `ShortCircuit` error instead of returning options with the `.help` attribute set. 35 | - **NEW:** Removed `argparse_help` and other shortcircuiting flags from the parser options object since they are no longer accessible anyway. 36 | - **NEW:** You can now access subcommand options with `parse()`. ([#60](https://github.com/iffy/nim-argparse/issues/60)) 37 | - **FIX:** Required options no longer override `-h`/`--help` shortcircuitry ([#57](https://github.com/iffy/nim-argparse/issues/57)) 38 | - **FIX:** Fix parsing of options that combine key and value into a single command-line token. 39 | 40 | # v1.1.0 - 2020-11-28 41 | 42 | - **NEW:** You can now use `--` to stop further flag/option parsing so that remaining command line parameters are considered arguments. 43 | - **NEW:** It's easy now to have the argument help text display the runtime name of the application using the special `{prog}` string within help text. To get the new runtime name behavior, call `newParser` without a string name. (Fixes [#33](https://github.com/iffy/nim-argparse/issues/33)) 44 | - **NEW:** Options can now be marked as required. ([#31](https://github.com/iffy/nim-argparse/issues/31)) 45 | - **FIX:** Works with Nim 1.0.x again. 46 | - **FIX:** Support hidden args again. 47 | - **FIX:** Add documentation and examples back. 48 | 49 | # v1.0.0 - 2020-11-26 50 | 51 | - **BREAKING CHANGE:** Default values for `option()` and `arg()` are now given as `Option` types. 52 | - **BREAKING CHANGE:** argparse no longer compiles on 1.0.x versions of Nim 53 | - **NEW:** Added a changelog! 54 | - **FIX:** Better error thrown when an option is lacking a value ([#29](https://github.com/iffy/nim-argparse/issues/29)) 55 | - **FIX:** Better error for when args are missing ([#18](https://github.com/iffy/nim-argparse/issues/18)) 56 | - **FIX:** You can now pass `-` as a value to args and options ([#40](https://github.com/iffy/nim-argparse/issues/40)) 57 | 58 | # v0.10.1 59 | 60 | - No changelog data prior to this. 61 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Matt Haggard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # argparse 2 | 3 | ![tests](https://github.com/iffy/nim-argparse/workflows/tests/badge.svg?branch=master) 4 | 5 | [Docs](https://www.iffycan.com/nim-argparse/argparse.html) 6 | 7 | Command line argument parsing library. It generates the parser at compile time so that parsed options have a well-defined type. 8 | 9 | # Example 10 | 11 | After defining your expected arguments with `newParser(...)`, use: 12 | 13 | 1. `run(...)` to parse and execute any `run:` blocks you've defined. This will automatically display help text when `-h`/`--help` is used. 14 | 2. `parse(...)` to parse without executing, giving you more control over what happens. 15 | 16 | Both procs will parse the process' command line if no arguments are given. 17 | 18 | [See the docs for more info](https://www.iffycan.com/nim-argparse/argparse.html) 19 | 20 | ## run() 21 | 22 | ```nim 23 | import argparse 24 | 25 | var p = newParser: 26 | flag("-a", "--apple") 27 | flag("-b", help="Show a banana") 28 | option("-o", "--output", help="Output to this file") 29 | command("somecommand"): 30 | arg("name") 31 | arg("others", nargs = -1) 32 | run: 33 | echo opts.name 34 | echo opts.others 35 | echo opts.parentOpts.apple 36 | echo opts.parentOpts.b 37 | echo opts.parentOpts.output 38 | echo opts.parentOpts.output_opt.get() 39 | 40 | try: 41 | p.run(@["--apple", "-o=foo", "somecommand", "myname", "thing1", "thing2"]) 42 | except UsageError as e: 43 | stderr.writeLine getCurrentExceptionMsg() 44 | quit(1) 45 | ``` 46 | 47 | ## parse() 48 | 49 | ```nim 50 | import argparse 51 | 52 | var p = newParser: 53 | flag("-a", "--apple") 54 | flag("-b", help="Show a banana") 55 | option("-o", "--output", help="Output to this file") 56 | arg("name") 57 | arg("others", nargs = -1) 58 | 59 | try: 60 | var opts = p.parse(@["--apple", "-o=foo", "hi"]) 61 | assert opts.apple == true 62 | assert opts.b == false 63 | assert opts.output == "foo" 64 | assert opts.name == "hi" 65 | assert opts.others == @[] 66 | except ShortCircuit as err: 67 | if err.flag == "argparse_help": 68 | echo err.help 69 | quit(1) 70 | except UsageError: 71 | stderr.writeLine getCurrentExceptionMsg() 72 | quit(1) 73 | ``` 74 | 75 | # Alternatives 76 | 77 | If `argparse` doesn't suit your needs, consider these alternatives: 78 | 79 | - 80 | - 81 | -------------------------------------------------------------------------------- /argparse.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "4.0.2" 4 | author = "Matt Haggard" 5 | description = "A command line argument parser" 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | 10 | # Dependencies 11 | 12 | requires "nim >= 1.0.10" 13 | -------------------------------------------------------------------------------- /builddocs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | nim doc --project --outdir:docs src/argparse.nim 4 | -------------------------------------------------------------------------------- /changes/README.md: -------------------------------------------------------------------------------- 1 | `changer` makes it easy to manage a `CHANGELOG.md` file. It works in Nim projects and other languages, too. 2 | 3 | # Installation 4 | 5 | ``` 6 | nimble install https://github.com/iffy/changer 7 | ``` 8 | 9 | # Configuration 10 | 11 | You can configure how `changer` behaves by editing the `changes/config.toml` file. 12 | 13 | # Usage 14 | 15 | Start a changelog in a project by running: 16 | 17 | changer init 18 | 19 | Every time you want to add something to the changelog, make a new Markdown file in `./changes/` named like this: 20 | 21 | - `fix-NAME.md` 22 | - `new-NAME.md` 23 | - `break-NAME.md` 24 | - `other-NAME.md` 25 | 26 | Use the tool to add a changelog entry: 27 | 28 | changer add 29 | 30 | When you're ready to release a new version, preview the new changelog with: 31 | 32 | changer bump -n 33 | 34 | Then make the new changelog (and update the version of any `.nimble` file): 35 | 36 | changer bump 37 | -------------------------------------------------------------------------------- /changes/config.toml: -------------------------------------------------------------------------------- 1 | update_nimble = true 2 | 3 | [[replacement]] 4 | pattern = '#(\d+)' 5 | replace = "[#$1](https://github.com/iffy/nim-argparse/issues/$1)" 6 | -------------------------------------------------------------------------------- /docs/argparse.idx: -------------------------------------------------------------------------------- 1 | newParser argparse.html#newParser.t,string,untyped argparse: newParser(name: string; body: untyped): untyped 2 | newParser argparse.html#newParser.t,untyped argparse: newParser(body: untyped): untyped 3 | flag argparse.html#flag,string,string,string argparse: flag(name1: string; name2 = ""; multiple = false; help = ""; hidden = false;\n shortcircuit = false) 4 | option argparse.html#option,string,string,string,string,seq[string] argparse: option(name1: string; name2 = ""; help = ""; default = none(); env = "";\n multiple = false; choices: seq[string] = @[]; required = false;\n hidden = false) 5 | arg argparse.html#arg,string,string,string,int argparse: arg(varname: string; default = none(); env = ""; help = ""; nargs = 1) 6 | help argparse.html#help,string argparse: help(helptext: string) 7 | nohelpflag argparse.html#nohelpflag argparse: nohelpflag() 8 | run argparse.html#run.t,untyped argparse: run(body: untyped): untyped 9 | command argparse.html#command.t,string,string,untyped argparse: command(name: string; group: string; content: untyped): untyped 10 | command argparse.html#command.t,string,untyped argparse: command(name: string; content: untyped): untyped 11 | -------------------------------------------------------------------------------- /docs/argparse/backend.idx: -------------------------------------------------------------------------------- 1 | UsageError argparse/backend.html#UsageError backend: UsageError 2 | ShortCircuit argparse/backend.html#ShortCircuit backend: ShortCircuit 3 | ArgFlag argparse/backend.html#ArgFlag ComponentKind.ArgFlag 4 | ArgOption argparse/backend.html#ArgOption ComponentKind.ArgOption 5 | ArgArgument argparse/backend.html#ArgArgument ComponentKind.ArgArgument 6 | ComponentKind argparse/backend.html#ComponentKind backend: ComponentKind 7 | Component argparse/backend.html#Component backend: Component 8 | Builder argparse/backend.html#Builder backend: Builder 9 | BuilderObj argparse/backend.html#BuilderObj backend: BuilderObj 10 | ParseState argparse/backend.html#ParseState backend: ParseState 11 | ARGPARSE_STDOUT argparse/backend.html#ARGPARSE_STDOUT backend: ARGPARSE_STDOUT 12 | builderStack argparse/backend.html#builderStack backend: builderStack 13 | toVarname argparse/backend.html#toVarname,string backend: toVarname(x: string): string 14 | `$` argparse/backend.html#$,ref.ParseState backend: `$`(state: ref ParseState): string 15 | newParseState argparse/backend.html#newParseState,openArray[string] backend: newParseState(args: openArray[string]): ref ParseState 16 | consume argparse/backend.html#consume,ref.ParseState,ComponentKind backend: consume(state: ref ParseState; thing: ComponentKind) 17 | skip argparse/backend.html#skip,ref.ParseState backend: skip(state: ref ParseState) 18 | popleft argparse/backend.html#popleft,seq[T] backend: popleft[T](s: var seq[T]): T 19 | popright argparse/backend.html#popright,seq[T],int backend: popright[T](s: var seq[T]; n = 0): T 20 | newBuilder argparse/backend.html#newBuilder,string backend: newBuilder(name = ""): Builder 21 | `$` argparse/backend.html#$,Builder backend: `$`(b: Builder): string 22 | optsTypeDef argparse/backend.html#optsTypeDef,Builder backend: optsTypeDef(b: Builder): NimNode 23 | parserTypeDef argparse/backend.html#parserTypeDef,Builder backend: parserTypeDef(b: Builder): NimNode 24 | raiseShortCircuit argparse/backend.html#raiseShortCircuit,string,string backend: raiseShortCircuit(flagname: string; help: string) 25 | parseProcDef argparse/backend.html#parseProcDef,Builder backend: parseProcDef(b: Builder): NimNode 26 | setOrAdd argparse/backend.html#setOrAdd,string,string backend: setOrAdd(x: var string; val: string) 27 | setOrAdd argparse/backend.html#setOrAdd,seq[string],string backend: setOrAdd(x: var seq[string]; val: string) 28 | getHelpText argparse/backend.html#getHelpText,Builder backend: getHelpText(b: Builder): string 29 | helpProcDef argparse/backend.html#helpProcDef,Builder backend: helpProcDef(b: Builder): NimNode 30 | GenResponse argparse/backend.html#GenResponse backend: GenResponse 31 | addParser argparse/backend.html#addParser,string,string,proc) backend: addParser(name: string; group: string; content: proc ()): Builder 32 | add_runProc argparse/backend.html#add_runProc,NimNode backend: add_runProc(body: NimNode) 33 | add_command argparse/backend.html#add_command,string,string,proc) backend: add_command(name: string; group: string; content: proc ()) 34 | allChildren argparse/backend.html#allChildren,Builder backend: allChildren(builder: Builder): seq[Builder] 35 | generateDefs argparse/backend.html#generateDefs,Builder backend: generateDefs(builder: Builder): NimNode 36 | -------------------------------------------------------------------------------- /docs/argparse/filler.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | src/argparse/filler 21 | 22 | 23 | 24 | 25 | 67 | 68 | 69 | 70 |
71 |
72 |

src/argparse/filler

73 |
74 |
75 |
76 | 80 |     Dark Mode 81 |
82 | 89 |
90 | Search: 92 |
93 |
94 | Group by: 95 | 99 |
100 | 184 | 185 |
186 | 187 |
188 |
189 | 190 |

191 |
192 |

Types

193 |
194 |
195 |
ArgFiller = object
196 |   slots: seq[Slot]
197 |   counts: CountTableRef[SlotKind]
198 | 
199 |
200 | 201 | 202 | 203 |
204 |
205 |
206 |
FillChannel = tuple[idx: Slice[int], dest: string, kind: SlotKind]
207 |
208 | 209 | 210 | 211 |
212 |
213 |
214 |
SlotKind = enum
215 |   Required, Optional, Wildcard
216 |
217 | 218 | 219 | 220 |
221 |
222 | 223 |
224 |
225 |

Procs

226 |
227 |
228 |
proc channels(filler; nargs: int): seq[FillChannel] {....raises: [], tags: [].}
229 |
230 | 231 | Given the number of arguments, show where those arguments will go 232 | 233 |
234 |
235 |
236 |
proc generate(filler; containerName: string): NimNode {....raises: [], tags: [].}
237 |
238 | 239 | 240 | 241 |
242 |
243 |
244 |
proc hasVariableArgs(filler): bool {....raises: [], tags: [].}
245 |
246 | 247 | 248 | 249 |
250 |
251 |
252 |
proc hasWildcard(filler): bool {....raises: [], tags: [].}
253 |
254 | 255 | 256 | 257 |
258 |
259 |
260 |
proc minArgs(filler): int {....raises: [], tags: [].}
261 |
262 | 263 | 264 | 265 |
266 |
267 |
268 |
proc missing(filler; nargs: int): seq[string] {....raises: [], tags: [].}
269 |
270 | 271 | Given the number of arguments, which required arguments will not get a value? 272 | 273 |
274 |
275 |
276 |
proc newArgFiller(): ref ArgFiller {....raises: [], tags: [].}
277 |
278 | 279 | 280 | 281 |
282 |
283 |
284 |
proc numArgsAfterWildcard(filler): int {....raises: [], tags: [].}
285 |
286 | 287 | 288 | 289 |
290 |
291 |
292 |
proc optional(filler; argname: string) {....raises: [], tags: [].}
293 |
294 | 295 | 296 | 297 |
298 |
299 |
300 |
proc required(filler; argname: string; nargs = 1) {....raises: [], tags: [].}
301 |
302 | 303 | 304 | 305 |
306 |
307 |
308 |
proc upperBreakpoint(filler): int {....raises: [], tags: [].}
309 |
310 | 311 | 312 | 313 |
314 |
315 |
316 |
proc wildcard(filler; argname: string) {....raises: [ValueError], tags: [].}
317 |
318 | 319 | 320 | 321 |
322 |
323 | 324 |
325 | 326 |
327 |
328 | 329 |
330 | 335 |
336 |
337 |
338 | 339 | 340 | 341 | -------------------------------------------------------------------------------- /docs/argparse/filler.idx: -------------------------------------------------------------------------------- 1 | Required argparse/filler.html#Required SlotKind.Required 2 | Optional argparse/filler.html#Optional SlotKind.Optional 3 | Wildcard argparse/filler.html#Wildcard SlotKind.Wildcard 4 | SlotKind argparse/filler.html#SlotKind filler: SlotKind 5 | ArgFiller argparse/filler.html#ArgFiller filler: ArgFiller 6 | FillChannel argparse/filler.html#FillChannel filler: FillChannel 7 | newArgFiller argparse/filler.html#newArgFiller filler: newArgFiller(): ref ArgFiller 8 | required argparse/filler.html#required,,string,int filler: required(filler; argname: string; nargs = 1) 9 | optional argparse/filler.html#optional,,string filler: optional(filler; argname: string) 10 | wildcard argparse/filler.html#wildcard,,string filler: wildcard(filler; argname: string) 11 | minArgs argparse/filler.html#minArgs filler: minArgs(filler): int 12 | numArgsAfterWildcard argparse/filler.html#numArgsAfterWildcard filler: numArgsAfterWildcard(filler): int 13 | hasVariableArgs argparse/filler.html#hasVariableArgs filler: hasVariableArgs(filler): bool 14 | hasWildcard argparse/filler.html#hasWildcard filler: hasWildcard(filler): bool 15 | upperBreakpoint argparse/filler.html#upperBreakpoint filler: upperBreakpoint(filler): int 16 | channels argparse/filler.html#channels,,int filler: channels(filler; nargs: int): seq[FillChannel] 17 | missing argparse/filler.html#missing,,int filler: missing(filler; nargs: int): seq[string] 18 | generate argparse/filler.html#generate,,string filler: generate(filler; containerName: string): NimNode 19 | -------------------------------------------------------------------------------- /docs/argparse/macrohelp.idx: -------------------------------------------------------------------------------- 1 | UnfinishedObjectTypeDef argparse/macrohelp.html#UnfinishedObjectTypeDef macrohelp: UnfinishedObjectTypeDef 2 | UnfinishedCase argparse/macrohelp.html#UnfinishedCase macrohelp: UnfinishedCase 3 | ident argparse/macrohelp.html#ident macrohelp: ident 4 | newIdentNode argparse/macrohelp.html#newIdentNode macrohelp: newIdentNode 5 | replaceNodes argparse/macrohelp.html#replaceNodes,NimNode macrohelp: replaceNodes(ast: NimNode): NimNode 6 | parentOf argparse/macrohelp.html#parentOf,NimNode,string macrohelp: parentOf(node: NimNode; name: string): InsertionPoint 7 | parentOf argparse/macrohelp.html#parentOf,NimNode,NimNode macrohelp: parentOf(node: NimNode; child: NimNode): InsertionPoint 8 | getInsertionPoint argparse/macrohelp.html#getInsertionPoint,NimNode,string macrohelp: getInsertionPoint(node: var NimNode; name: string): InsertionPoint 9 | clear argparse/macrohelp.html#clear,InsertionPoint macrohelp: clear(point: InsertionPoint): int 10 | replace argparse/macrohelp.html#replace,InsertionPoint,NimNode macrohelp: replace(point: InsertionPoint; newnode: NimNode) 11 | newObjectTypeDef argparse/macrohelp.html#newObjectTypeDef,string,bool macrohelp: newObjectTypeDef(name: string; isref: bool = false): UnfinishedObjectTypeDef 12 | addObjectField argparse/macrohelp.html#addObjectField,UnfinishedObjectTypeDef,string,NimNode macrohelp: addObjectField(objtypedef: UnfinishedObjectTypeDef; name: string; kind: NimNode) 13 | addObjectField argparse/macrohelp.html#addObjectField,UnfinishedObjectTypeDef,string,string,bool macrohelp: addObjectField(objtypedef: UnfinishedObjectTypeDef; name: string; kind: string;\n isref: bool = false) 14 | newCaseStatement argparse/macrohelp.html#newCaseStatement,NimNode macrohelp: newCaseStatement(key: NimNode): ref UnfinishedCase 15 | newCaseStatement argparse/macrohelp.html#newCaseStatement,string macrohelp: newCaseStatement(key: string): ref UnfinishedCase 16 | add argparse/macrohelp.html#add,ref.UnfinishedCase,seq[NimNode],NimNode macrohelp: add(n: ref UnfinishedCase; opt: seq[NimNode]; body: NimNode) 17 | add argparse/macrohelp.html#add,ref.UnfinishedCase,NimNode,NimNode macrohelp: add(n: ref UnfinishedCase; opt: NimNode; body: NimNode) 18 | add argparse/macrohelp.html#add,ref.UnfinishedCase,string,NimNode macrohelp: add(n: ref UnfinishedCase; opt: string; body: NimNode) 19 | add argparse/macrohelp.html#add,ref.UnfinishedCase,seq[string],NimNode macrohelp: add(n: ref UnfinishedCase; opts: seq[string]; body: NimNode) 20 | add argparse/macrohelp.html#add,ref.UnfinishedCase,int,NimNode macrohelp: add(n: ref UnfinishedCase; opt: int; body: NimNode) 21 | hasElse argparse/macrohelp.html#hasElse,ref.UnfinishedCase macrohelp: hasElse(n: ref UnfinishedCase): bool 22 | addElse argparse/macrohelp.html#addElse,ref.UnfinishedCase,NimNode macrohelp: addElse(n: ref UnfinishedCase; body: NimNode) 23 | isValid argparse/macrohelp.html#isValid,ref.UnfinishedCase macrohelp: isValid(n: ref UnfinishedCase): bool 24 | finalize argparse/macrohelp.html#finalize,ref.UnfinishedCase macrohelp: finalize(n: ref UnfinishedCase): NimNode 25 | newIfStatement argparse/macrohelp.html#newIfStatement macrohelp: newIfStatement(): ref UnfinishedIf 26 | add argparse/macrohelp.html#add,ref.UnfinishedIf,NimNode,NimNode macrohelp: add(n: ref UnfinishedIf; cond: NimNode; body: NimNode) 27 | addElse argparse/macrohelp.html#addElse,ref.UnfinishedIf,NimNode macrohelp: addElse(n: ref UnfinishedIf; body: NimNode) 28 | isValid argparse/macrohelp.html#isValid,ref.UnfinishedIf macrohelp: isValid(n: ref UnfinishedIf): bool 29 | finalize argparse/macrohelp.html#finalize,ref.UnfinishedIf macrohelp: finalize(n: ref UnfinishedIf): NimNode 30 | nimRepr argparse/macrohelp.html#nimRepr,NimNode macrohelp: nimRepr(n: NimNode): string 31 | -------------------------------------------------------------------------------- /docs/argparse/nimdoc.out.css: -------------------------------------------------------------------------------- 1 | /* 2 | Stylesheet for use with Docutils/rst2html. 3 | 4 | See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to 5 | customize this style sheet. 6 | 7 | Modified from Chad Skeeters' rst2html-style 8 | https://bitbucket.org/cskeeters/rst2html-style/ 9 | 10 | Modified by Boyd Greenfield and narimiran 11 | */ 12 | 13 | :root { 14 | --primary-background: #fff; 15 | --secondary-background: ghostwhite; 16 | --third-background: #e8e8e8; 17 | --border: #dde; 18 | --text: #222; 19 | --anchor: #07b; 20 | --anchor-focus: #607c9f; 21 | --input-focus: #1fa0eb; 22 | --strong: #3c3c3c; 23 | --hint: #9A9A9A; 24 | --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAAA9CAYAAADCt9ebAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjAzOjQ4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1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"); 25 | 26 | --keyword: #5e8f60; 27 | --identifier: #222; 28 | --comment: #484a86; 29 | --operator: #155da4; 30 | --punctuation: black; 31 | --other: black; 32 | --escapeSequence: #c4891b; 33 | --number: #252dbe; 34 | --literal: #a4255b; 35 | --raw-data: #a4255b; 36 | } 37 | 38 | [data-theme="dark"] { 39 | --primary-background: #171921; 40 | --secondary-background: #1e202a; 41 | --third-background: #2b2e3b; 42 | --border: #0e1014; 43 | --text: #fff; 44 | --anchor: #8be9fd; 45 | --anchor-focus: #8be9fd; 46 | --input-focus: #8be9fd; 47 | --strong: #bd93f9; 48 | --hint: #7A7C85; 49 | --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQy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"); 50 | 51 | --keyword: #ff79c6; 52 | --identifier: #f8f8f2; 53 | --comment: #6272a4; 54 | --operator: #ff79c6; 55 | --punctuation: #f8f8f2; 56 | --other: #f8f8f2; 57 | --escapeSequence: #bd93f9; 58 | --number: #bd93f9; 59 | --literal: #f1fa8c; 60 | --raw-data: #8be9fd; 61 | } 62 | 63 | .theme-switch-wrapper { 64 | display: flex; 65 | align-items: center; 66 | 67 | em { 68 | margin-left: 10px; 69 | font-size: 1rem; 70 | } 71 | } 72 | .theme-switch { 73 | display: inline-block; 74 | height: 22px; 75 | position: relative; 76 | width: 50px; 77 | } 78 | 79 | .theme-switch input { 80 | display: none; 81 | } 82 | 83 | .slider { 84 | background-color: #ccc; 85 | bottom: 0; 86 | cursor: pointer; 87 | left: 0; 88 | position: absolute; 89 | right: 0; 90 | top: 0; 91 | transition: .4s; 92 | } 93 | 94 | .slider:before { 95 | background-color: #fff; 96 | bottom: 4px; 97 | content: ""; 98 | height: 13px; 99 | left: 4px; 100 | position: absolute; 101 | transition: .4s; 102 | width: 13px; 103 | } 104 | 105 | input:checked + .slider { 106 | background-color: #66bb6a; 107 | } 108 | 109 | input:checked + .slider:before { 110 | transform: translateX(26px); 111 | } 112 | 113 | .slider.round { 114 | border-radius: 17px; 115 | } 116 | 117 | .slider.round:before { 118 | border-radius: 50%; 119 | } 120 | 121 | html { 122 | font-size: 100%; 123 | -webkit-text-size-adjust: 100%; 124 | -ms-text-size-adjust: 100%; } 125 | 126 | body { 127 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; 128 | font-weight: 400; 129 | font-size: 1.125em; 130 | line-height: 1.5; 131 | color: var(--text); 132 | background-color: var(--primary-background); } 133 | 134 | /* Skeleton grid */ 135 | .container { 136 | position: relative; 137 | width: 100%; 138 | max-width: 1050px; 139 | margin: 0 auto; 140 | padding: 0; 141 | box-sizing: border-box; } 142 | 143 | .column, 144 | .columns { 145 | width: 100%; 146 | float: left; 147 | box-sizing: border-box; 148 | margin-left: 1%; 149 | } 150 | 151 | .column:first-child, 152 | .columns:first-child { 153 | margin-left: 0; } 154 | 155 | .three.columns { 156 | width: 19%; } 157 | 158 | .nine.columns { 159 | width: 80.0%; } 160 | 161 | .twelve.columns { 162 | width: 100%; 163 | margin-left: 0; } 164 | 165 | @media screen and (max-width: 860px) { 166 | .three.columns { 167 | display: none; 168 | } 169 | .nine.columns { 170 | width: 98.0%; 171 | } 172 | body { 173 | font-size: 1em; 174 | line-height: 1.35; 175 | } 176 | } 177 | 178 | cite { 179 | font-style: italic !important; } 180 | 181 | 182 | /* Nim search input */ 183 | div#searchInputDiv { 184 | margin-bottom: 1em; 185 | } 186 | input#searchInput { 187 | width: 80%; 188 | } 189 | 190 | /* 191 | * Some custom formatting for input forms. 192 | * This also fixes input form colors on Firefox with a dark system theme on Linux. 193 | */ 194 | input { 195 | -moz-appearance: none; 196 | background-color: var(--secondary-background); 197 | color: var(--text); 198 | border: 1px solid var(--border); 199 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; 200 | font-size: 0.9em; 201 | padding: 6px; 202 | } 203 | 204 | input:focus { 205 | border: 1px solid var(--input-focus); 206 | box-shadow: 0 0 3px var(--input-focus); 207 | } 208 | 209 | select { 210 | -moz-appearance: none; 211 | background-color: var(--secondary-background); 212 | color: var(--text); 213 | border: 1px solid var(--border); 214 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; 215 | font-size: 0.9em; 216 | padding: 6px; 217 | } 218 | 219 | select:focus { 220 | border: 1px solid var(--input-focus); 221 | box-shadow: 0 0 3px var(--input-focus); 222 | } 223 | 224 | /* Docgen styles */ 225 | /* Links */ 226 | a { 227 | color: var(--anchor); 228 | text-decoration: none; 229 | } 230 | 231 | a span.Identifier { 232 | text-decoration: underline; 233 | text-decoration-color: #aab; 234 | } 235 | 236 | a.reference-toplevel { 237 | font-weight: bold; 238 | } 239 | 240 | a.toc-backref { 241 | text-decoration: none; 242 | color: var(--text); } 243 | 244 | a.link-seesrc { 245 | color: #607c9f; 246 | font-size: 0.9em; 247 | font-style: italic; } 248 | 249 | a:hover, 250 | a:focus { 251 | color: var(--anchor-focus); 252 | text-decoration: underline; } 253 | 254 | a:hover span.Identifier { 255 | color: var(--anchor); 256 | } 257 | 258 | 259 | sub, 260 | sup { 261 | position: relative; 262 | font-size: 75%; 263 | line-height: 0; 264 | vertical-align: baseline; } 265 | 266 | sup { 267 | top: -0.5em; } 268 | 269 | sub { 270 | bottom: -0.25em; } 271 | 272 | img { 273 | width: auto; 274 | height: auto; 275 | max-width: 100%; 276 | vertical-align: middle; 277 | border: 0; 278 | -ms-interpolation-mode: bicubic; } 279 | 280 | @media print { 281 | * { 282 | color: black !important; 283 | text-shadow: none !important; 284 | background: transparent !important; 285 | box-shadow: none !important; } 286 | 287 | a, 288 | a:visited { 289 | text-decoration: underline; } 290 | 291 | a[href]:after { 292 | content: " (" attr(href) ")"; } 293 | 294 | abbr[title]:after { 295 | content: " (" attr(title) ")"; } 296 | 297 | .ir a:after, 298 | a[href^="javascript:"]:after, 299 | a[href^="#"]:after { 300 | content: ""; } 301 | 302 | pre, 303 | blockquote { 304 | border: 1px solid #999; 305 | page-break-inside: avoid; } 306 | 307 | thead { 308 | display: table-header-group; } 309 | 310 | tr, 311 | img { 312 | page-break-inside: avoid; } 313 | 314 | img { 315 | max-width: 100% !important; } 316 | 317 | @page { 318 | margin: 0.5cm; } 319 | 320 | h1 { 321 | page-break-before: always; } 322 | 323 | h1.title { 324 | page-break-before: avoid; } 325 | 326 | p, 327 | h2, 328 | h3 { 329 | orphans: 3; 330 | widows: 3; } 331 | 332 | h2, 333 | h3 { 334 | page-break-after: avoid; } 335 | } 336 | 337 | 338 | p { 339 | margin-top: 0.5em; 340 | margin-bottom: 0.5em; 341 | } 342 | 343 | small { 344 | font-size: 85%; } 345 | 346 | strong { 347 | font-weight: 600; 348 | font-size: 0.95em; 349 | color: var(--strong); 350 | } 351 | 352 | em { 353 | font-style: italic; } 354 | 355 | h1 { 356 | font-size: 1.8em; 357 | font-weight: 400; 358 | padding-bottom: .25em; 359 | border-bottom: 6px solid var(--third-background); 360 | margin-top: 2.5em; 361 | margin-bottom: 1em; 362 | line-height: 1.2em; } 363 | 364 | h1.title { 365 | padding-bottom: 1em; 366 | border-bottom: 0px; 367 | font-size: 2.5em; 368 | text-align: center; 369 | font-weight: 900; 370 | margin-top: 0.75em; 371 | margin-bottom: 0em; 372 | } 373 | 374 | h2 { 375 | font-size: 1.3em; 376 | margin-top: 2em; } 377 | 378 | h2.subtitle { 379 | text-align: center; } 380 | 381 | h3 { 382 | font-size: 1.125em; 383 | font-style: italic; 384 | margin-top: 1.5em; } 385 | 386 | h4 { 387 | font-size: 1.125em; 388 | margin-top: 1em; } 389 | 390 | h5 { 391 | font-size: 1.125em; 392 | margin-top: 0.75em; } 393 | 394 | h6 { 395 | font-size: 1.1em; } 396 | 397 | 398 | ul, 399 | ol { 400 | padding: 0; 401 | margin-top: 0.5em; 402 | margin-left: 0.75em; } 403 | 404 | ul ul, 405 | ul ol, 406 | ol ol, 407 | ol ul { 408 | margin-bottom: 0; 409 | margin-left: 1.25em; } 410 | 411 | li { 412 | list-style-type: circle; 413 | } 414 | 415 | ul.simple-boot li { 416 | list-style-type: none; 417 | margin-left: 0em; 418 | margin-bottom: 0.5em; 419 | } 420 | 421 | ol.simple > li, ul.simple > li { 422 | margin-bottom: 0.25em; 423 | margin-left: 0.4em } 424 | 425 | ul.simple.simple-toc > li { 426 | margin-top: 1em; 427 | } 428 | 429 | ul.simple-toc { 430 | list-style: none; 431 | font-size: 0.9em; 432 | margin-left: -0.3em; 433 | margin-top: 1em; } 434 | 435 | ul.simple-toc > li { 436 | list-style-type: none; 437 | } 438 | 439 | ul.simple-toc-section { 440 | list-style-type: circle; 441 | margin-left: 1em; 442 | color: #6c9aae; } 443 | 444 | 445 | ol.arabic { 446 | list-style: decimal; } 447 | 448 | ol.loweralpha { 449 | list-style: lower-alpha; } 450 | 451 | ol.upperalpha { 452 | list-style: upper-alpha; } 453 | 454 | ol.lowerroman { 455 | list-style: lower-roman; } 456 | 457 | ol.upperroman { 458 | list-style: upper-roman; } 459 | 460 | ul.auto-toc { 461 | list-style-type: none; } 462 | 463 | 464 | dl { 465 | margin-bottom: 1.5em; } 466 | 467 | dt { 468 | margin-bottom: -0.5em; 469 | margin-left: 0.0em; } 470 | 471 | dd { 472 | margin-left: 2.0em; 473 | margin-bottom: 3.0em; 474 | margin-top: 0.5em; } 475 | 476 | 477 | hr { 478 | margin: 2em 0; 479 | border: 0; 480 | border-top: 1px solid #aaa; } 481 | 482 | blockquote { 483 | font-size: 0.9em; 484 | font-style: italic; 485 | padding-left: 0.5em; 486 | margin-left: 0; 487 | border-left: 5px solid #bbc; 488 | } 489 | 490 | .pre { 491 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; 492 | font-weight: 500; 493 | font-size: 0.85em; 494 | color: var(--text); 495 | background-color: var(--third-background); 496 | padding-left: 3px; 497 | padding-right: 3px; 498 | border-radius: 4px; 499 | } 500 | 501 | pre { 502 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; 503 | color: var(--text); 504 | font-weight: 500; 505 | display: inline-block; 506 | box-sizing: border-box; 507 | min-width: 100%; 508 | padding: 0.5em; 509 | margin-top: 0.5em; 510 | margin-bottom: 0.5em; 511 | font-size: 0.85em; 512 | white-space: pre !important; 513 | overflow-y: hidden; 514 | overflow-x: visible; 515 | background-color: var(--secondary-background); 516 | border: 1px solid var(--border); 517 | -webkit-border-radius: 6px; 518 | -moz-border-radius: 6px; 519 | border-radius: 6px; } 520 | 521 | .pre-scrollable { 522 | max-height: 340px; 523 | overflow-y: scroll; } 524 | 525 | 526 | /* Nim line-numbered tables */ 527 | .line-nums-table { 528 | width: 100%; 529 | table-layout: fixed; } 530 | 531 | table.line-nums-table { 532 | border-radius: 4px; 533 | border: 1px solid #cccccc; 534 | background-color: ghostwhite; 535 | border-collapse: separate; 536 | margin-top: 15px; 537 | margin-bottom: 25px; } 538 | 539 | .line-nums-table tbody { 540 | border: none; } 541 | 542 | .line-nums-table td pre { 543 | border: none; 544 | background-color: transparent; } 545 | 546 | .line-nums-table td.blob-line-nums { 547 | width: 28px; } 548 | 549 | .line-nums-table td.blob-line-nums pre { 550 | color: #b0b0b0; 551 | -webkit-filter: opacity(75%); 552 | text-align: right; 553 | border-color: transparent; 554 | background-color: transparent; 555 | padding-left: 0px; 556 | margin-left: 0px; 557 | padding-right: 0px; 558 | margin-right: 0px; } 559 | 560 | 561 | table { 562 | max-width: 100%; 563 | background-color: transparent; 564 | margin-top: 0.5em; 565 | margin-bottom: 1.5em; 566 | border-collapse: collapse; 567 | border-color: var(--third-background); 568 | border-spacing: 0; 569 | font-size: 0.9em; 570 | } 571 | 572 | table th, table td { 573 | padding: 0px 0.5em 0px; 574 | border-color: var(--third-background); 575 | } 576 | 577 | table th { 578 | background-color: var(--third-background); 579 | border-color: var(--third-background); 580 | font-weight: bold; } 581 | 582 | table th.docinfo-name { 583 | background-color: transparent; 584 | } 585 | 586 | table tr:hover { 587 | background-color: var(--third-background); } 588 | 589 | 590 | /* rst2html default used to remove borders from tables and images */ 591 | .borderless, table.borderless td, table.borderless th { 592 | border: 0; } 593 | 594 | table.borderless td, table.borderless th { 595 | /* Override padding for "table.docutils td" with "! important". 596 | The right padding separates the table cells. */ 597 | padding: 0 0.5em 0 0 !important; } 598 | 599 | .first { 600 | /* Override more specific margin styles with "! important". */ 601 | margin-top: 0 !important; } 602 | 603 | .last, .with-subtitle { 604 | margin-bottom: 0 !important; } 605 | 606 | .hidden { 607 | display: none; } 608 | 609 | blockquote.epigraph { 610 | margin: 2em 5em; } 611 | 612 | dl.docutils dd { 613 | margin-bottom: 0.5em; } 614 | 615 | object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { 616 | overflow: hidden; } 617 | 618 | 619 | div.figure { 620 | margin-left: 2em; 621 | margin-right: 2em; } 622 | 623 | div.footer, div.header { 624 | clear: both; 625 | text-align: center; 626 | color: #666; 627 | font-size: smaller; } 628 | 629 | div.footer { 630 | padding-top: 5em; 631 | } 632 | 633 | div.line-block { 634 | display: block; 635 | margin-top: 1em; 636 | margin-bottom: 1em; } 637 | 638 | div.line-block div.line-block { 639 | margin-top: 0; 640 | margin-bottom: 0; 641 | margin-left: 1.5em; } 642 | 643 | div.topic { 644 | margin: 2em; } 645 | 646 | div.search_results { 647 | background-color: antiquewhite; 648 | margin: 3em; 649 | padding: 1em; 650 | border: 1px solid #4d4d4d; 651 | } 652 | 653 | div#global-links ul { 654 | margin-left: 0; 655 | list-style-type: none; 656 | } 657 | 658 | div#global-links > simple-boot { 659 | margin-left: 3em; 660 | } 661 | 662 | hr.docutils { 663 | width: 75%; } 664 | 665 | img.align-left, .figure.align-left, object.align-left { 666 | clear: left; 667 | float: left; 668 | margin-right: 1em; } 669 | 670 | img.align-right, .figure.align-right, object.align-right { 671 | clear: right; 672 | float: right; 673 | margin-left: 1em; } 674 | 675 | img.align-center, .figure.align-center, object.align-center { 676 | display: block; 677 | margin-left: auto; 678 | margin-right: auto; } 679 | 680 | .align-left { 681 | text-align: left; } 682 | 683 | .align-center { 684 | clear: both; 685 | text-align: center; } 686 | 687 | .align-right { 688 | text-align: right; } 689 | 690 | /* reset inner alignment in figures */ 691 | div.align-right { 692 | text-align: inherit; } 693 | 694 | p.attribution { 695 | text-align: right; 696 | margin-left: 50%; } 697 | 698 | p.caption { 699 | font-style: italic; } 700 | 701 | p.credits { 702 | font-style: italic; 703 | font-size: smaller; } 704 | 705 | p.label { 706 | white-space: nowrap; } 707 | 708 | p.rubric { 709 | font-weight: bold; 710 | font-size: larger; 711 | color: maroon; 712 | text-align: center; } 713 | 714 | p.topic-title { 715 | font-weight: bold; } 716 | 717 | pre.address { 718 | margin-bottom: 0; 719 | margin-top: 0; 720 | font: inherit; } 721 | 722 | pre.literal-block, pre.doctest-block, pre.math, pre.code { 723 | margin-left: 2em; 724 | margin-right: 2em; } 725 | 726 | pre.code .ln { 727 | color: grey; } 728 | 729 | /* line numbers */ 730 | pre.code, code { 731 | background-color: #eeeeee; } 732 | 733 | pre.code .comment, code .comment { 734 | color: #5c6576; } 735 | 736 | pre.code .keyword, code .keyword { 737 | color: #3B0D06; 738 | font-weight: bold; } 739 | 740 | pre.code .literal.string, code .literal.string { 741 | color: #0c5404; } 742 | 743 | pre.code .name.builtin, code .name.builtin { 744 | color: #352b84; } 745 | 746 | pre.code .deleted, code .deleted { 747 | background-color: #DEB0A1; } 748 | 749 | pre.code .inserted, code .inserted { 750 | background-color: #A3D289; } 751 | 752 | span.classifier { 753 | font-style: oblique; } 754 | 755 | span.classifier-delimiter { 756 | font-weight: bold; } 757 | 758 | span.option { 759 | white-space: nowrap; } 760 | 761 | span.problematic { 762 | color: #b30000; } 763 | 764 | span.section-subtitle { 765 | /* font-size relative to parent (h1..h6 element) */ 766 | font-size: 80%; } 767 | 768 | span.DecNumber { 769 | color: var(--number); } 770 | 771 | span.BinNumber { 772 | color: var(--number); } 773 | 774 | span.HexNumber { 775 | color: var(--number); } 776 | 777 | span.OctNumber { 778 | color: var(--number); } 779 | 780 | span.FloatNumber { 781 | color: var(--number); } 782 | 783 | span.Identifier { 784 | color: var(--identifier); } 785 | 786 | span.Keyword { 787 | font-weight: 600; 788 | color: var(--keyword); } 789 | 790 | span.StringLit { 791 | color: var(--literal); } 792 | 793 | span.LongStringLit { 794 | color: var(--literal); } 795 | 796 | span.CharLit { 797 | color: var(--literal); } 798 | 799 | span.EscapeSequence { 800 | color: var(--escapeSequence); } 801 | 802 | span.Operator { 803 | color: var(--operator); } 804 | 805 | span.Punctuation { 806 | color: var(--punctuation); } 807 | 808 | span.Comment, span.LongComment { 809 | font-style: italic; 810 | font-weight: 400; 811 | color: var(--comment); } 812 | 813 | span.RegularExpression { 814 | color: darkviolet; } 815 | 816 | span.TagStart { 817 | color: darkviolet; } 818 | 819 | span.TagEnd { 820 | color: darkviolet; } 821 | 822 | span.Key { 823 | color: #252dbe; } 824 | 825 | span.Value { 826 | color: #252dbe; } 827 | 828 | span.RawData { 829 | color: var(--raw-data); } 830 | 831 | span.Assembler { 832 | color: #252dbe; } 833 | 834 | span.Preprocessor { 835 | color: #252dbe; } 836 | 837 | span.Directive { 838 | color: #252dbe; } 839 | 840 | span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, 841 | span.Other { 842 | color: var(--other); } 843 | 844 | /* Pop type, const, proc, and iterator defs in nim def blocks */ 845 | dt pre > span.Identifier, dt pre > span.Operator { 846 | color: var(--identifier); 847 | font-weight: 700; } 848 | 849 | dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier, 850 | dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier { 851 | color: var(--identifier); 852 | font-weight: inherit; } 853 | 854 | /* Nim sprite for the footer (taken from main page favicon) */ 855 | .nim-sprite { 856 | display: inline-block; 857 | width: 51px; 858 | height: 14px; 859 | background-position: 0 0; 860 | background-size: 51px 14px; 861 | -webkit-filter: opacity(50%); 862 | background-repeat: no-repeat; 863 | background-image: var(--nim-sprite-base64); 864 | margin-bottom: 5px; } 865 | 866 | span.pragmadots { 867 | /* Position: relative frees us up to make the dots 868 | look really nice without fucking up the layout and 869 | causing bulging in the parent container */ 870 | position: relative; 871 | /* 1px down looks slightly nicer */ 872 | top: 1px; 873 | padding: 2px; 874 | background-color: var(--third-background); 875 | border-radius: 4px; 876 | margin: 0 2px; 877 | cursor: pointer; 878 | font-size: 0.8em; 879 | } 880 | 881 | span.pragmadots:hover { 882 | background-color: var(--hint); 883 | } 884 | span.pragmawrap { 885 | display: none; 886 | } 887 | 888 | span.attachedType { 889 | display: none; 890 | visibility: hidden; 891 | } -------------------------------------------------------------------------------- /docs/theindex.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Index 21 | 22 | 23 | 24 | 25 | 67 | 68 | 69 | 70 |
71 |
72 |

Index

73 | Modules: argparse, argparse/backend, argparse/filler, argparse/macrohelp.

API symbols

74 |
`$`:
80 |
add:
94 |
add_command:
98 |
addElse:
104 |
addObjectField:
112 |
addParser:
116 |
add_runProc:
120 |
allChildren:
124 |
arg:
128 |
ArgArgument:
132 |
ArgFiller:
136 |
ArgFlag:
140 |
ArgOption:
144 |
ARGPARSE_STDOUT:
148 |
Builder:
152 |
BuilderObj:
156 |
builderStack:
160 |
channels:
164 |
clear:
168 |
command:
174 |
Component:
178 |
ComponentKind:
182 |
consume:
186 |
FillChannel:
190 |
finalize:
196 |
flag:
202 |
generate:
206 |
generateDefs:
210 |
GenResponse:
214 |
getHelpText:
218 |
getInsertionPoint:
222 |
hasElse:
226 |
hasVariableArgs:
230 |
hasWildcard:
234 |
help:
238 |
helpProcDef:
242 |
ident:
246 |
isValid:
252 |
minArgs:
256 |
missing:
260 |
newArgFiller:
264 |
newBuilder:
268 |
newCaseStatement:
274 |
newIdentNode:
278 |
newIfStatement:
282 |
newObjectTypeDef:
286 |
newParser:
292 |
newParseState:
296 |
nimRepr:
300 |
nohelpflag:
304 |
numArgsAfterWildcard:
308 |
option:
316 |
Optional:
320 |
optional:
324 |
optsTypeDef:
328 |
parentOf:
334 |
parseProcDef:
338 |
parserTypeDef:
342 |
ParseState:
346 |
popleft:
350 |
popright:
354 |
raiseShortCircuit:
358 |
replace:
362 |
replaceNodes:
366 |
Required:
370 |
required:
374 |
run:
378 |
setOrAdd:
384 |
ShortCircuit:
388 |
skip:
392 |
SlotKind:
396 |
toVarname:
400 |
UnfinishedCase:
404 |
UnfinishedObjectTypeDef:
408 |
upperBreakpoint:
412 |
UsageError:
416 |
Wildcard:
420 |
wildcard:
424 |
425 |
426 | 431 |
432 |
433 |
434 | 435 | 436 | 437 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /src/argparse.nim: -------------------------------------------------------------------------------- 1 | ## argparse is an explicit, strongly-typed command line argument parser. 2 | ## 3 | ## Use ``newParser`` to create a parser. Within the body 4 | ## of the parser use the following procs/templates (read the individual 5 | ## documentation below for more details): 6 | ## 7 | ## =================== =================================================== 8 | ## Proc Description 9 | ## =================== =================================================== 10 | ## ``flag(...)`` boolean flag (e.g. ``--dryrun``) 11 | ## ``option(...)`` option with argument (e.g. ``--output foo``) 12 | ## ``arg(...)`` positional argument (e.g. ``file1 file2``) 13 | ## ``help(...)`` add a help string to the parser or subcommand 14 | ## ``command "NAME":`` add a sub command 15 | ## ``run:`` code to run when the parser is used in run mode 16 | ## ``nohelpflag()`` disable the automatic ``-h/--help`` flag 17 | ## =================== =================================================== 18 | ## 19 | ## The following special variables are available within ``run`` blocks: 20 | ## 21 | ## - ``opts`` - contains your user-defined options. Same thing as returned from ``parse(...)`` scoped to the subcommand. 22 | ## - ``opts.parentOpts`` - a reference to parent options (i.e. from a subcommand) 23 | ## - ``opts.argparse_command`` - a string holding the chosen command 24 | ## - ``opts.command`` - same as above (if there is no flag/option/arg named ``"command"``) 25 | ## - ``opts.argparse_NAMEOFCOMMAND_opts`` - an ``Option[...]`` that will hold the options for the command named ``NAMEOFCOMMAND`` 26 | ## - ``opts.NAMEOFCOMMAND`` - Same as above, but a shorter version (if there's no name conflict with other flags/options/args) 27 | ## 28 | ## If ``Parser.parse()`` and ``Parser.run()`` are called without arguments, they use the arguments from the command line. 29 | ## 30 | ## By default (unless ``nohelpflag`` is present) calling ``parse()`` with a help 31 | ## flag (``-h`` / ``--help``) will raise a ``ShortCircuit`` error. The error's ``flag`` 32 | ## field will contain the name of the flag that triggered the short circuit. 33 | ## For help-related short circuits, the error's ``help`` field will contain the help text 34 | ## of the given subcommand. 35 | ## 36 | runnableExamples: 37 | var res:string 38 | var p = newParser: 39 | help("A demonstration of this library in a program named {prog}") 40 | flag("-n", "--dryrun") 41 | option("--name", default=some("bob"), help = "Name to use") 42 | command("ls"): 43 | run: 44 | res = "did ls " & opts.parentOpts.name 45 | command("run"): 46 | option("-c", "--command") 47 | run: 48 | let name = opts.parentOpts.name 49 | if opts.parentOpts.dryrun: 50 | res = "would have run: " & opts.command & " " & name 51 | else: 52 | res = "ran " & opts.command & " " & name 53 | try: 54 | p.run(@["-n", "run", "--command", "something"]) 55 | except UsageError: 56 | stderr.writeLine getCurrentExceptionMsg() 57 | quit(1) 58 | assert res == "would have run: something bob" 59 | 60 | runnableExamples: 61 | var p = newParser: 62 | help("A description of this program, named {prog}") 63 | flag("-n", "--dryrun") 64 | option("-o", "--output", help="Write output to this file", default=some("somewhere.txt")) 65 | option("-k", "--kind", choices = @["fruit", "vegetable"]) 66 | arg("input") 67 | 68 | try: 69 | let opts = p.parse(@["-n", "--output", "another.txt", "cranberry"]) 70 | assert opts.dryrun == true 71 | assert opts.output == "another.txt" 72 | assert opts.input == "cranberry" 73 | except ShortCircuit as err: 74 | if err.flag == "argparse_help": 75 | echo err.help 76 | quit(1) 77 | except UsageError: 78 | stderr.writeLine getCurrentExceptionMsg() 79 | quit(1) 80 | 81 | runnableExamples: 82 | var p = newParser: 83 | command "go": 84 | flag("-a") 85 | command "leave": 86 | flag("-b") 87 | 88 | let opts = p.parse(@["go", "-a"]) 89 | assert opts.command == "go" 90 | assert opts.go.isSome 91 | assert opts.go.get.a == true 92 | assert opts.leave.isNone 93 | 94 | import std/macros 95 | import strutils 96 | import argparse/backend; export backend 97 | import argparse/macrohelp; export macrohelp 98 | 99 | proc longAndShort(name1: string, name2: string): tuple[long: string, short: string] = 100 | ## Given two strings, return the longer and shorter of the two with 101 | ## shortname possibly being empty. 102 | var 103 | longname: string 104 | shortname: string 105 | if name2 == "": 106 | longname = name1 107 | else: 108 | if name1.len > name2.len: 109 | longname = name1 110 | shortname = name2 111 | else: 112 | longname = name2 113 | shortname = name1 114 | return (longname, shortname) 115 | 116 | template newParser*(name: string, body: untyped): untyped = 117 | ## Create a new parser with a static program name. 118 | ## 119 | runnableExamples: 120 | var p = newParser("my parser"): 121 | help("'{prog}' == 'my parser'") 122 | flag("-a") 123 | assert p.parse(@["-a"]).a == true 124 | 125 | macro domkParser() : untyped {.gensym.} = 126 | let builder = addParser(name, "", proc() = body) 127 | builder.generateDefs() 128 | domkParser() 129 | 130 | template newParser*(body: untyped): untyped = 131 | ## Create a new command-line parser named the same as the current executable. 132 | ## 133 | runnableExamples: 134 | var p = newParser: 135 | flag("-a") 136 | assert p.parse(@["-a"]).a == true 137 | 138 | macro domkParser(): untyped = 139 | let builder = addParser("", "", proc() = body) 140 | builder.generateDefs() 141 | domkParser() 142 | 143 | proc flag*(name1: string, name2 = "", multiple = false, help = "", hidden = false, shortcircuit = false) {.compileTime.} = 144 | ## Add a boolean flag to the argument parser. The boolean 145 | ## will be available on the parsed options object as the 146 | ## longest named flag. 147 | ## 148 | ## If ``multiple`` is true then the flag can be specified multiple 149 | ## times and the datatype will be an int. 150 | ## 151 | ## If ``hidden`` is true then the flag usage is not shown in the help. 152 | ## 153 | ## If ``shortcircuit`` is true, then when the flag is encountered during 154 | ## processing, the parser will immediately raise a ``ShortCircuit`` error 155 | ## with the ``flag`` attribute set to this flag's name. This is how the 156 | ## default help flag is implemented. 157 | ## 158 | ## ``help`` is additional help text for this flag. 159 | runnableExamples: 160 | var p = newParser("Some Thing"): 161 | flag("--show-name", help="Show the name") 162 | flag("-a", help="Some flag named a") 163 | flag("-n", "--dryrun", help="Don't actually run") 164 | 165 | let opts = p.parse(@["--show-name", "-n"]) 166 | assert opts.show_name == true 167 | assert opts.a == false 168 | assert opts.dryrun == true 169 | 170 | let names = longAndShort(name1, name2) 171 | let varname = names.long.toVarname() 172 | builderStack[^1].components.add Component( 173 | kind: ArgFlag, 174 | help: help, 175 | varname: varname, 176 | flagShort: names.short, 177 | flagLong: names.long, 178 | flagMultiple: multiple, 179 | shortCircuit: shortcircuit, 180 | hidden: hidden, 181 | ) 182 | 183 | proc option*(name1: string, name2 = "", help = "", default = none[string](), env = "", multiple = false, choices: seq[string] = @[], required = false, hidden = false) {.compileTime.} = 184 | ## Add an option to the argument parser. The longest 185 | ## named flag will be used as the name on the parsed 186 | ## result. 187 | ## 188 | ## Additionally, an ``Option[string]`` named ``FLAGNAME_opt`` 189 | ## will be available on the parse result. 190 | ## 191 | ## Set ``multiple`` to true to accept multiple options. 192 | ## 193 | ## Set ``default`` to the default string value. 194 | ## If the value can't be inferred at compile-time, insert it in the ``run`` 195 | ## block while accesing the option with 196 | ## ``opts.FLAGNAME_opt.get(otherwise = RunTimeString)`` instead. 197 | ## 198 | ## Set ``env`` to an environment variable name to use as the default value 199 | ## 200 | ## Set ``choices`` to restrict the possible choices. 201 | ## 202 | ## Set ``required = true`` if this is a required option. Yes, calling 203 | ## it a "required option" is a paradox :) 204 | ## 205 | ## Set ``hidden`` to prevent the option usage listing in the help text. 206 | ## 207 | ## ``help`` is additional help text for this option. 208 | runnableExamples: 209 | var p = newParser: 210 | option("-a", "--apple", help="Name of apple") 211 | assert p.parse(@["-a", "5"]).apple == "5" 212 | assert p.parse(@[]).apple_opt.isNone 213 | assert p.parse(@["--apple", "6"]).apple_opt.get() == "6" 214 | 215 | let names = longAndShort(name1, name2) 216 | let varname = names.long.toVarname() 217 | builderStack[^1].components.add Component( 218 | kind: ArgOption, 219 | help: help, 220 | hidden: hidden, 221 | varname: varname, 222 | env: env, 223 | optShort: names.short, 224 | optLong: names.long, 225 | optMultiple: multiple, 226 | optDefault: default, 227 | optChoices: choices, 228 | optRequired: required, 229 | ) 230 | 231 | proc arg*(varname: string, default = none[string](), env = "", help = "", nargs = 1) {.compileTime.} = 232 | ## Add an argument to the argument parser. 233 | ## 234 | ## Set ``default`` to the default ``Option[string]`` value. This is only 235 | ## allowed for ``nargs = 1``. 236 | ## 237 | ## Set ``env`` to an environment variable name to use as the default value. This is only allowed for ``nargs = 1``. 238 | ## 239 | ## The value ``nargs`` has the following meanings: 240 | ## 241 | ## - ``nargs = 1`` : A single argument. The value type will be ``string`` 242 | ## - ``nargs = 2`` (or more) : Accept a specific number of arguments. The value type will be ``seq[string]`` 243 | ## - ``nargs = -1`` : Accept 0 or more arguments. Only one ``nargs = -1`` ``arg()`` is allowed per parser/command. 244 | ## 245 | ## ``help`` is additional help text for this argument. 246 | runnableExamples: 247 | var p = newParser: 248 | arg("name", help = "Name of apple") 249 | arg("twowords", nargs = 2) 250 | arg("more", nargs = -1) 251 | let res = p.parse(@["cameo", "hot", "dog", "things"]) 252 | assert res.name == "cameo" 253 | assert res.twowords == @["hot", "dog"] 254 | assert res.more == @["things"] 255 | 256 | builderStack[^1].components.add Component( 257 | kind: ArgArgument, 258 | help: help, 259 | varname: varname.toVarname(), 260 | nargs: nargs, 261 | env: env, 262 | argDefault: default, 263 | ) 264 | 265 | proc help*(helptext: string) {.compileTime.} = 266 | ## Add help to a parser or subcommand. 267 | ## 268 | ## You may use the special string ``{prog}`` within any help text, and it 269 | ## will be replaced by the program name. 270 | ## 271 | runnableExamples: 272 | var p = newParser: 273 | help("Some helpful description") 274 | command("dostuff"): 275 | help("More helpful information") 276 | echo p.help 277 | 278 | builderStack[^1].help &= helptext 279 | 280 | proc nohelpflag*() {.compileTime.} = 281 | ## Disable the automatic ``-h``/``--help`` flag 282 | runnableExamples: 283 | var p = newParser: 284 | nohelpflag() 285 | 286 | builderStack[^1].components.del(0) 287 | 288 | template run*(body: untyped): untyped = 289 | ## Add a run block to this command 290 | runnableExamples: 291 | var p = newParser: 292 | command("dostuff"): 293 | run: 294 | echo "Actually do stuff" 295 | 296 | add_runproc(replaceNodes(quote(body))) 297 | 298 | template command*(name: string, group: string, content: untyped): untyped = 299 | ## Add a subcommand to this parser 300 | ## 301 | ## ``group`` is a string used to group commands in help output 302 | runnableExamples: 303 | var p = newParser: 304 | command("dostuff", "groupA"): discard 305 | command("morestuff", "groupB"): discard 306 | command("morelikethefirst", "groupA"): discard 307 | echo p.help 308 | add_command(name, group) do (): 309 | content 310 | 311 | template command*(name: string, content: untyped): untyped = 312 | ## Add a subcommand to this parser 313 | runnableExamples: 314 | var p = newParser: 315 | command("dostuff"): 316 | run: 317 | echo "Actually do stuff" 318 | p.run(@["dostuff"]) 319 | command(name, "", content) 320 | 321 | -------------------------------------------------------------------------------- /src/argparse/backend.nim: -------------------------------------------------------------------------------- 1 | import algorithm; export algorithm 2 | import macros 3 | import options; export options 4 | import sequtils; export sequtils 5 | import streams; export streams 6 | import strformat 7 | import strutils; export strutils 8 | import tables 9 | import os; export os 10 | 11 | import ./macrohelp 12 | import ./filler 13 | 14 | type 15 | UsageError* = object of ValueError 16 | ShortCircuit* = object of CatchableError 17 | flag*: string 18 | help*: string 19 | 20 | ComponentKind* = enum 21 | ArgFlag 22 | ArgOption 23 | ArgArgument 24 | 25 | Component* = object 26 | varname*: string 27 | hidden*: bool 28 | help*: string 29 | env*: string 30 | case kind*: ComponentKind 31 | of ArgFlag: 32 | flagShort*: string 33 | flagLong*: string 34 | flagMultiple*: bool 35 | shortCircuit*: bool 36 | of ArgOption: 37 | optShort*: string 38 | optLong*: string 39 | optMultiple*: bool 40 | optDefault*: Option[string] 41 | optChoices*: seq[string] 42 | optRequired*: bool 43 | of ArgArgument: 44 | nargs*: int 45 | argDefault*: Option[string] 46 | 47 | Builder* = ref BuilderObj 48 | BuilderObj* {.acyclic.} = object 49 | ## A compile-time object used to accumulate parser options 50 | ## before building the parser 51 | name*: string 52 | ## Command name for subcommand parsers, or program name for 53 | ## the parent parser. 54 | symbol*: string 55 | ## Unique tag to apply to Parser and Option types to avoid 56 | ## conflicts. By default, this is generated with Nim's 57 | ## gensym algorithm. 58 | components*: seq[Component] 59 | help*: string 60 | groupName*: string 61 | children*: seq[Builder] 62 | parent*: Option[Builder] 63 | runProcBodies*: seq[NimNode] 64 | 65 | ParseState* = object 66 | tokens*: seq[string] 67 | cursor*: int 68 | extra*: seq[string] 69 | ## tokens that weren't parsed 70 | done*: bool 71 | token*: Option[string] 72 | ## The current unprocessed token 73 | key*: Option[string] 74 | ## The current key (possibly the head of a 'key=value' token) 75 | value*: Option[string] 76 | ## The current value (possibly the tail of a 'key=value' token) 77 | valuePartOfToken*: bool 78 | ## true if the value is part of the current token (e.g. 'key=value') 79 | runProcs*: seq[proc()] 80 | ## Procs to be run at the end of parsing 81 | 82 | var ARGPARSE_STDOUT* = newFileStream(stdout) 83 | var builderStack* {.compileTime.} = newSeq[Builder]() 84 | 85 | proc toVarname*(x: string): string = 86 | ## Convert x to something suitable as a Nim identifier 87 | ## Replaces - with _ for instance 88 | x.replace("-", "_").strip(chars={'_'}) 89 | 90 | #-------------------------------------------------------------- 91 | # ParseState 92 | #-------------------------------------------------------------- 93 | 94 | proc `$`*(state: ref ParseState): string {.inline.} = $(state[]) 95 | 96 | proc advance(state: ref ParseState, amount: int, skip = false) = 97 | ## Advance the parse by `amount` tokens 98 | ## 99 | ## If `skip` is given, add the passed-over tokens to `extra` 100 | for i in 0..= state.tokens.len: 102 | continue 103 | if skip: 104 | state.extra.add(state.tokens[state.cursor]) 105 | state.cursor.inc() 106 | if state.cursor >= state.tokens.len: 107 | state.done = true 108 | state.token = none[string]() 109 | state.key = none[string]() 110 | state.value = none[string]() 111 | state.valuePartOfToken = false 112 | else: 113 | let token = state.tokens[state.cursor] 114 | state.token = some(token) 115 | if token.startsWith("-") and '=' in token: 116 | let parts = token.split("=", 1) 117 | state.key = some(parts[0]) 118 | state.value = some(parts[1]) 119 | state.valuePartOfToken = true 120 | else: 121 | state.key = some(token) 122 | state.valuePartOfToken = false 123 | if (state.cursor + 1) < state.tokens.len: 124 | state.value = some(state.tokens[state.cursor + 1]) 125 | else: 126 | state.value = none[string]() 127 | 128 | proc newParseState*(args: openArray[string]): ref ParseState = 129 | new(result) 130 | result.tokens = toSeq(args) 131 | result.extra = newSeq[string]() 132 | result.cursor = -1 133 | result.advance(1) 134 | 135 | proc consume*(state: ref ParseState, thing: ComponentKind) = 136 | ## Advance the parser, marking some tokens as consumed. 137 | case thing 138 | of ArgFlag: 139 | state.advance(1) 140 | of ArgOption: 141 | state.advance(if state.valuePartOfToken: 1 else: 2) 142 | of ArgArgument: 143 | state.advance(1) 144 | 145 | proc skip*(state: ref ParseState) {.inline.} = 146 | state.advance(1, skip = true) 147 | 148 | #-------------------------------------------------------------- 149 | # General 150 | #-------------------------------------------------------------- 151 | 152 | proc safeIdentStr(x: string): string = 153 | ## Remove components of a string that make it unsuitable as a Nim identifier 154 | for c in x: 155 | case c 156 | of '_': 157 | if result.len >= 1 and result[result.len-1] != '_': 158 | result.add c 159 | of 'A'..'Z', 'a'..'z', '\x80'..'\xff': 160 | result.add c 161 | of '0'..'9': 162 | if result.len >= 1: 163 | result.add c 164 | else: 165 | discard 166 | result.strip(chars = {'_'}) 167 | 168 | proc popleft*[T](s: var seq[T]):T = 169 | ## Pop from the front of a seq 170 | result = s[0] 171 | when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0): 172 | s.delete(0..0) 173 | else: 174 | s.delete(0, 0) 175 | 176 | proc popright*[T](s: var seq[T], n = 0): T = 177 | ## Pop the nth item from the end of a seq 178 | let idx = s.len - n - 1 179 | result = s[idx] 180 | when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0): 181 | s.delete(idx..idx) 182 | else: 183 | s.delete(idx, idx) 184 | 185 | #-------------------------------------------------------------- 186 | # Component 187 | #-------------------------------------------------------------- 188 | 189 | proc identDef(varname: NimNode, vartype: NimNode): NimNode = 190 | ## Return a property definition for an object. 191 | ## 192 | ## type 193 | ## Foo = object 194 | ## varname*: vartype <-- this is the AST being returned 195 | return nnkIdentDefs.newTree( 196 | nnkPostfix.newTree( 197 | ident("*"), 198 | varname, 199 | ), 200 | vartype, 201 | newEmptyNode() 202 | ) 203 | 204 | proc propDefinitions(c: Component): seq[NimNode] = 205 | ## Return the type of this component as will be put in the 206 | ## parser return type object definition 207 | ## 208 | ## type 209 | ## Foo = object 210 | ## name*: string <-- this is the AST being returned 211 | let varname = ident(c.varname.safeIdentStr) 212 | case c.kind 213 | of ArgFlag: 214 | if c.flagMultiple: 215 | result.add identDef(varname, ident("int")) 216 | else: 217 | result.add identDef(varname, ident("bool")) 218 | of ArgOption: 219 | if c.optMultiple: 220 | result.add identDef(varname, parseExpr("seq[string]")) 221 | else: 222 | result.add identDef(varname, ident("string")) 223 | result.add identDef( 224 | ident(safeIdentStr(c.varname & "_opt")), 225 | nnkBracketExpr.newTree( 226 | ident("Option"), 227 | ident("string") 228 | ) 229 | ) 230 | of ArgArgument: 231 | if c.nargs != 1: 232 | result.add identDef(varname, parseExpr("seq[string]")) 233 | else: 234 | result.add identDef(varname, ident("string")) 235 | 236 | #-------------------------------------------------------------- 237 | # Builder 238 | #-------------------------------------------------------------- 239 | 240 | proc newBuilder*(name = ""): Builder = 241 | new(result) 242 | result.name = name 243 | result.symbol = genSym(nskLet, if name == "": "Argparse" else: name.safeIdentStr).toStrLit.strVal 244 | result.children = newSeq[Builder]() 245 | result.runProcBodies = newSeq[NimNode]() 246 | result.components.add Component( 247 | kind: ArgFlag, 248 | varname: "argparse_help", 249 | shortCircuit: true, 250 | flagShort: "-h", 251 | flagLong: "--help", 252 | ) 253 | 254 | proc `$`*(b: Builder): string = $(b[]) 255 | 256 | proc optsIdent(b: Builder): NimNode = 257 | ## Name of the option type for this Builder 258 | # let name = if b.name == "": "Argparse" else: b.name 259 | ident("Opts" & b.symbol) 260 | 261 | proc parserIdent(b: Builder): NimNode = 262 | ## Name of the parser type for this Builder 263 | # let name = if b.name == "": "Argparse" else: b.name 264 | ident("Parser" & b.symbol) 265 | 266 | proc optsTypeDef*(b: Builder): NimNode = 267 | ## Generate the type definition for the return value of parsing: 268 | var properties = nnkRecList.newTree() 269 | for component in b.components: 270 | if component.kind == ArgFlag: 271 | if component.shortCircuit: 272 | # don't add shortcircuits to the option type 273 | continue 274 | properties.add(component.propDefinitions()) 275 | if b.parent.isSome: 276 | properties.add nnkIdentDefs.newTree( 277 | nnkPostfix.newTree( 278 | ident("*"), 279 | ident("parentOpts") 280 | ), 281 | nnkRefTy.newTree( 282 | b.parent.get().optsIdent, 283 | ), 284 | newEmptyNode() 285 | ) 286 | 287 | if b.children.len > 0: 288 | # .argparse_command 289 | properties.add nnkIdentDefs.newTree( 290 | nnkPostfix.newTree( 291 | ident("*"), 292 | ident("argparse_command"), 293 | ), 294 | ident("string"), 295 | newEmptyNode(), 296 | ) 297 | 298 | # subcommand opts 299 | for child in b.children: 300 | let childOptsIdent = child.optsIdent() 301 | properties.add nnkIdentDefs.newTree( 302 | nnkPostfix.newTree( 303 | ident("*"), 304 | ident("argparse_" & child.name.toVarname() & "_opts") 305 | ), 306 | nnkBracketExpr.newTree( 307 | ident("Option"), 308 | nnkRefTy.newTree(childOptsIdent) 309 | ), 310 | newEmptyNode() 311 | ) 312 | 313 | # type MyOpts = object 314 | result = nnkTypeDef.newTree( 315 | b.optsIdent(), 316 | newEmptyNode(), 317 | nnkObjectTy.newTree( 318 | newEmptyNode(), 319 | newEmptyNode(), 320 | properties, 321 | ) 322 | ) 323 | 324 | proc parserTypeDef*(b: Builder): NimNode = 325 | ## Generate the type definition for the Parser object: 326 | ## 327 | ## type 328 | ## MyParser = object 329 | result = nnkTypeDef.newTree( 330 | b.parserIdent(), 331 | newEmptyNode(), 332 | nnkObjectTy.newTree( 333 | newEmptyNode(), 334 | newEmptyNode(), 335 | newEmptyNode(), 336 | ) 337 | ) 338 | 339 | proc raiseShortCircuit*(flagname: string, help: string) {.inline.} = 340 | var e: ref ShortCircuit 341 | new(e) 342 | e.flag = flagname 343 | e.msg = "ShortCircuit on " & flagname 344 | e.help = help 345 | raise e 346 | 347 | proc parseProcDef*(b: Builder): NimNode = 348 | ## Generate the parse proc for this Builder 349 | ## 350 | ## proc parse(p: MyParser, args: seq[string]): MyOpts = 351 | result = newStmtList() 352 | 353 | let parserIdent = b.parserIdent() 354 | let optsIdent = b.optsIdent() 355 | 356 | # flag/opt/arg handlers 357 | var flagCase = newCaseStatement(parseExpr("token")) 358 | var optCase = newCaseStatement(parseExpr("key")) 359 | var requiredOptionGuard = newStmtList() 360 | var setDefaults = newStmtList() 361 | var filler = newArgFiller() 362 | for component in b.components: 363 | case component.kind 364 | of ArgFlag: 365 | var matches: seq[string] 366 | if component.flagShort != "": 367 | matches.add(component.flagShort) # of "-h": 368 | if component.flagLong != "": 369 | matches.add(component.flagLong) # of "--help": 370 | var body = newStmtList() 371 | if component.shortCircuit: 372 | let varname = newStrLitNode(component.varname) 373 | body.add quote do: 374 | raiseShortCircuit(`varname`, parser.help) 375 | else: 376 | if component.flagMultiple: 377 | let varname = ident(component.varname) 378 | body.add quote do: 379 | opts.`varname`.inc() 380 | state.consume(ArgFlag) 381 | continue 382 | else: 383 | let varname = ident(component.varname) 384 | body.add quote do: 385 | opts.`varname` = true 386 | state.consume(ArgFlag) 387 | continue 388 | if not body.isNil: 389 | flagCase.add(matches, body) 390 | of ArgOption: 391 | let varname = ident(component.varname) 392 | let varname_opt = ident(component.varname & "_opt") 393 | if component.env != "": 394 | # Set default from environment variable 395 | let dft = newStrLitNode(component.optDefault.get("")) 396 | let env = newStrLitNode(component.env) 397 | setDefaults.add quote do: 398 | opts.`varname` = getEnv(`env`, `dft`) 399 | if component.optDefault.isSome: 400 | setDefaults.add quote do: 401 | opts.`varname_opt` = some(getEnv(`env`, `dft`)) 402 | elif component.optDefault.isSome: 403 | # Set default 404 | let dft = component.optDefault.get() 405 | setDefaults.add quote do: 406 | opts.`varname` = `dft` 407 | opts.`varname_opt` = some(`dft`) 408 | var matches: seq[string] 409 | var optCombo: string 410 | if component.optShort != "": 411 | matches.add(component.optShort) # of "-h" 412 | optCombo.add component.optShort 413 | if component.optLong != "": 414 | matches.add(component.optLong) # of "--help" 415 | if optCombo != "": 416 | optCombo.add "," 417 | optCombo.add(component.optLong) 418 | let optComboNode = newStrLitNode(optCombo) 419 | 420 | # Make sure it has a value 421 | let valueGuard = quote do: 422 | if state.value.isNone: 423 | raise UsageError.newException("Missing value for " & `optComboNode`) 424 | 425 | # Make sure it in the set of expected choices 426 | var choiceGuard = parseExpr("discard \"no choice guard\"") 427 | if component.optChoices.len > 0: 428 | let choices = component.optChoices 429 | choiceGuard = quote do: 430 | if state.value.get() notin `choices`: 431 | raise UsageError.newException("Invalid value for " & `optComboNode` & ": " & state.value.get() & " (valid choices: " & $`choices` & ")") 432 | 433 | # Make sure required options have been provided 434 | if component.optRequired: 435 | let envStr = newStrLitNode(component.env) 436 | requiredOptionGuard.add quote do: 437 | if `optComboNode` notin switches_seen and (`envStr` == "" or getEnv(`envStr`) == ""): 438 | raise UsageError.newException("Option " & `optComboNode` & " is required and was not provided") 439 | 440 | # Make sure it hasn't been provided twice 441 | var duplicateGuard: NimNode 442 | var body: NimNode 443 | if component.optMultiple: 444 | # -o apple -o banana 445 | duplicateGuard = parseExpr("discard \"no duplicate guard\"") 446 | body = quote do: 447 | opts.`varname`.add(state.value.get()) 448 | state.consume(ArgOption) 449 | continue 450 | else: 451 | # -o single 452 | duplicateGuard = quote do: 453 | if `optComboNode` in switches_seen: 454 | raise UsageError.newException("Option " & `optComboNode` & " supplied multiple times") 455 | switches_seen.add(`optComboNode`) 456 | body = quote do: 457 | opts.`varname` = state.value.get() 458 | opts.`varname_opt` = some(opts.`varname`) 459 | state.consume(ArgOption) 460 | continue 461 | if not body.isNil: 462 | optCase.add(matches, newStmtList( 463 | valueGuard, 464 | choiceGuard, 465 | duplicateGuard, 466 | body, 467 | )) 468 | of ArgArgument: 469 | # Process positional arguments 470 | if component.nargs == -1: 471 | filler.wildcard(component.varname) 472 | elif component.nargs == 1: 473 | let varname = ident(component.varname) 474 | if component.env != "": 475 | filler.optional(component.varname) 476 | let envStr = newStrLitNode(component.env) 477 | let dftStr = newStrLitNode(component.argDefault.get("")) 478 | setDefaults.add replaceNodes(quote do: 479 | opts.`varname` = getEnv(`envStr`, `dftStr`) 480 | ) 481 | elif component.argDefault.isSome: 482 | filler.optional(component.varname) 483 | let dftStr = newStrLitNode(component.argDefault.get()) 484 | setDefaults.add replaceNodes(quote do: 485 | opts.`varname` = `dftStr` 486 | ) 487 | else: 488 | filler.required(component.varname, 1) 489 | elif component.nargs > 1: 490 | filler.required(component.varname, component.nargs) 491 | 492 | # args proc 493 | let minArgs = newIntLitNode(filler.minArgs) 494 | var argcase = newCaseStatement(parseExpr("state.extra.len")) 495 | if filler.minArgs > 0: 496 | for nargs in 0..= `minArgs`: 596 | `commandCase_node` 597 | if token == "--": 598 | doneProcessingFlags = true 599 | state.consume(ArgArgument) 600 | continue 601 | state.skip() 602 | if not argsTaken: 603 | takeArgsFromExtra(opts, state) 604 | if state.extra.len > 0: 605 | # There are extra args. 606 | raise UsageError.newException("Unknown argument(s): " & state.extra.join(", ")) 607 | `runProcs` 608 | except ShortCircuit as e: 609 | if e.flag == "argparse_help" and runblocks: 610 | output.write(parser.help()) 611 | if quitOnHelp: 612 | quit(1) 613 | else: 614 | raise e 615 | 616 | result.add(replaceNodes(parseProc)) 617 | 618 | # Convenience parse/run procs 619 | result.add replaceNodes(quote do: 620 | proc parse(parser: `parserIdent`, args: seq[string], quitOnHelp = true): ref `optsIdent` {.used.} = 621 | ## Parse arguments using the `parserIdent` parser 622 | var state = newParseState(args) 623 | var opts: ref `optsIdent` 624 | new(opts) 625 | parser.parse(opts, state, quitOnHelp = quitOnHelp) 626 | result = opts 627 | ) 628 | # proc parse() with no args 629 | result.add replaceNodes(quote do: 630 | proc parse(parser: `parserIdent`, quitOnHelp = true): ref `optsIdent` {.used.} = 631 | ## Parse command line params 632 | when declared(commandLineParams): 633 | parser.parse(toSeq(commandLineParams()), quitOnHelp = quitOnHelp) 634 | else: 635 | var params: seq[string] 636 | for i in 0..paramCount(): 637 | params.add(paramStr(i)) 638 | parser.parse(params, quitOnHelp = quitOnHelp) 639 | ) 640 | result.add replaceNodes(quote do: 641 | proc run(parser: `parserIdent`, args: seq[string], quitOnHelp = true, output:Stream = ARGPARSE_STDOUT) {.used.} = 642 | ## Run the matching run-blocks of the parser 643 | var state = newParseState(args) 644 | var opts: ref `optsIdent` 645 | new(opts) 646 | parser.parse(opts, state, runblocks = true, quitOnHelp = quitOnHelp, output = output) 647 | ) 648 | # proc run() with no args 649 | result.add replaceNodes(quote do: 650 | proc run(parser: `parserIdent`) {.used.} = 651 | ## Run the matching run-blocks of the parser 652 | when declared(commandLineParams): 653 | parser.run(toSeq(commandLineParams())) 654 | else: 655 | var params: seq[string] 656 | for i in 0..paramCount(): 657 | params.add(paramStr(i)) 658 | parser.run(params) 659 | ) 660 | 661 | # Shorter named convenience procs 662 | if b.children.len > 0: 663 | # .argparse_command -> .command shortcut 664 | result.add replaceNodes(quote do: 665 | proc command(opts: ref `optsIdent`): string {.used, inline.} = 666 | opts.argparse_command 667 | ) 668 | 669 | # .argparse_NAME_opts -> .NAME shortcut 670 | for child in b.children: 671 | let name = ident(child.name) 672 | let fulloptname = ident("argparse_" & child.name.toVarname & "_opts") 673 | let retval = nnkBracketExpr.newTree( 674 | ident("Option"), 675 | nnkRefTy.newTree(child.optsIdent()) 676 | ) 677 | result.add replaceNodes(quote do: 678 | proc `name`(opts: ref `optsIdent`): `retval` {.used, inline.} = 679 | opts.`fulloptname` 680 | ) 681 | 682 | proc setOrAdd*(x: var string, val: string) = 683 | x = val 684 | 685 | proc setOrAdd*(x: var seq[string], val: string) = 686 | x.add(val) 687 | 688 | proc getHelpText*(b: Builder): string = 689 | ## Generate the static help text string 690 | if b.help != "": 691 | result.add(b.help) 692 | result.add("\L\L") 693 | 694 | # usage 695 | var usage_parts:seq[string] 696 | 697 | proc firstline(s:string):string = 698 | s.split("\L")[0] 699 | 700 | proc formatOption(flags:string, helptext:string, defaultval = none[string](), envvar:string = "", choices:seq[string] = @[], opt_width = 26, max_width = 100):string = 701 | result.add(" " & flags) 702 | var helptext = helptext 703 | if choices.len > 0: 704 | helptext.add(" Possible values: [" & choices.join(", ") & "]") 705 | if defaultval.isSome: 706 | helptext.add(&" (default: {defaultval.get()})") 707 | if envvar != "": 708 | helptext.add(&" (env: {envvar})") 709 | helptext = helptext.strip() 710 | if helptext != "": 711 | if flags.len > opt_width: 712 | result.add("\L") 713 | result.add(" ") 714 | result.add(" ".repeat(opt_width+1)) 715 | result.add(helptext) 716 | else: 717 | result.add(" ".repeat(opt_width - flags.len)) 718 | result.add(" ") 719 | result.add(helptext) 720 | 721 | var opts = "" 722 | var args = "" 723 | 724 | # Options and Arguments 725 | for comp in b.components: 726 | case comp.kind 727 | of ArgFlag: 728 | if not comp.hidden: 729 | var flag_parts: seq[string] 730 | if comp.flagShort != "": 731 | flag_parts.add(comp.flagShort) 732 | if comp.flagLong != "": 733 | flag_parts.add(comp.flagLong) 734 | opts.add(formatOption(flag_parts.join(", "), comp.help)) 735 | opts.add("\L") 736 | of ArgOption: 737 | if not comp.hidden: 738 | var flag_parts: seq[string] 739 | if comp.optShort != "": 740 | flag_parts.add(comp.optShort) 741 | if comp.optLong != "": 742 | flag_parts.add(comp.optLong) 743 | var flags = flag_parts.join(", ") & "=" & comp.varname.toUpper() 744 | opts.add(formatOption(flags, comp.help, defaultval = comp.optDefault, envvar = comp.env, choices = comp.optChoices)) 745 | opts.add("\L") 746 | of ArgArgument: 747 | var leftside:string 748 | if comp.nargs == 1: 749 | leftside = comp.varname 750 | if comp.argDefault.isSome: 751 | leftside = &"[{comp.varname}]" 752 | elif comp.nargs == -1: 753 | leftside = &"[{comp.varname} ...]" 754 | else: 755 | leftside = (&"{comp.varname} ").repeat(comp.nargs) 756 | usage_parts.add(leftside) 757 | args.add(formatOption(leftside, comp.help, defaultval = comp.argDefault, envvar = comp.env, opt_width=16)) 758 | args.add("\L") 759 | 760 | var commands = newOrderedTable[string,string](2) 761 | 762 | if b.children.len > 0: 763 | usage_parts.add("COMMAND") 764 | for subbuilder in b.children: 765 | var leftside = subbuilder.name 766 | let group = subbuilder.groupName 767 | if not commands.hasKey(group): 768 | commands[group] = "" 769 | let indent = if group == "": "" else: " " 770 | commands[group].add(indent & formatOption(leftside, subbuilder.help.firstline, opt_width=16)) 771 | commands[group].add("\L") 772 | 773 | if usage_parts.len > 0 or opts != "": 774 | result.add("Usage:\L") 775 | result.add(" ") 776 | result.add(b.name & " ") 777 | if opts != "": 778 | result.add("[options] ") 779 | result.add(usage_parts.join(" ")) 780 | result.add("\L\L") 781 | 782 | if commands.len == 1: 783 | let key = toSeq(commands.keys())[0] 784 | result.add("Commands:\L\L") 785 | result.add(commands[key]) 786 | result.add("\L") 787 | elif commands.len > 0: 788 | result.add("Commands:\L\L") 789 | for key in commands.keys(): 790 | result.add(" " & key & ":\L\L") 791 | result.add(commands[key]) 792 | result.add("\L") 793 | 794 | if args != "": 795 | result.add("Arguments:\L") 796 | result.add(args) 797 | result.add("\L") 798 | 799 | if opts != "": 800 | result.add("Options:\L") 801 | result.add(opts) 802 | result.add("\L") 803 | 804 | result.stripLineEnd() 805 | 806 | proc helpProcDef*(b: Builder): NimNode = 807 | ## Generate the help proc for the parser 808 | let helptext = b.getHelpText() 809 | let prog = newStrLitNode(b.name) 810 | let parserIdent = b.parserIdent() 811 | result = newStmtList() 812 | result.add replaceNodes(quote do: 813 | proc help(parser: `parserIdent`): string {.used.} = 814 | ## Get the help string for this parser 815 | var prog = `prog` 816 | if prog == "": 817 | prog = getAppFilename().extractFilename() 818 | result.add `helptext`.replace("{prog}", prog) 819 | ) 820 | 821 | type 822 | GenResponse* = tuple 823 | types: NimNode 824 | procs: NimNode 825 | instance: NimNode 826 | 827 | proc addParser*(name: string, group: string, content: proc()): Builder = 828 | ## Add a parser (whether main parser or subcommand) and return the Builder 829 | ## Call ``generateDefs`` to get the type and proc definitions. 830 | builderStack.add newBuilder(name) 831 | content() 832 | var builder = builderStack.pop() 833 | builder.groupName = group 834 | if builder.help == "" and builderStack.len == 0: 835 | builder.help = "{prog}" 836 | 837 | if builderStack.len > 0: 838 | # subcommand 839 | builderStack[^1].children.add(builder) 840 | builder.parent = some(builderStack[^1]) 841 | 842 | return builder 843 | 844 | proc add_runProc*(body: NimNode) {.compileTime.} = 845 | ## Add a run block proc to the current parser 846 | builderStack[^1].runProcBodies.add(replaceNodes(body)) 847 | 848 | proc add_command*(name: string, group: string, content: proc()) {.compileTime.} = 849 | ## Add a subcommand to a parser 850 | discard addParser(name, group, content) 851 | 852 | proc allChildren*(builder: Builder): seq[Builder] = 853 | ## Return all the descendents of this builder 854 | for child in builder.children: 855 | result.add child 856 | result.add child.allChildren() 857 | 858 | proc generateDefs*(builder: Builder): NimNode = 859 | ## Generate the AST definitions for the current builder 860 | result = newStmtList() 861 | var typeSection = nnkTypeSection.newTree() 862 | var procsSection = newStmtList() 863 | 864 | # children first to avoid forward declarations 865 | for child in builder.allChildren().reversed: 866 | typeSection.add child.optsTypeDef() 867 | typeSection.add child.parserTypeDef() 868 | procsSection.add child.helpProcDef() 869 | procsSection.add child.parseProcDef() 870 | 871 | # MyOpts = object 872 | typeSection.add builder.optsTypeDef() 873 | # MyParser = object 874 | typeSection.add builder.parserTypeDef() 875 | 876 | # proc help(p: MyParser, ...) 877 | # proc parse(p: MyParser, ...) 878 | # proc run(p: MyParser, ...) 879 | procsSection.add builder.helpProcDef() 880 | procsSection.add builder.parseProcDef() 881 | 882 | # let parser = MyParser() 883 | # parser 884 | let parserIdent = builder.parserIdent() 885 | let instantiationSection = quote do: 886 | var parser = `parserIdent`() 887 | parser 888 | 889 | result.add(typeSection) 890 | result.add(procsSection) 891 | result.add(instantiationSection) 892 | -------------------------------------------------------------------------------- /src/argparse/filler.nim: -------------------------------------------------------------------------------- 1 | import tables 2 | 3 | type 4 | SlotKind* = enum 5 | Required 6 | Optional 7 | Wildcard 8 | 9 | Slot = object 10 | name: string 11 | case kind*: SlotKind 12 | of Required: 13 | nargs: int 14 | else: 15 | discard 16 | 17 | ArgFiller* = object 18 | slots: seq[Slot] 19 | counts: CountTableRef[SlotKind] 20 | 21 | FillChannel* = tuple 22 | idx: Slice[int] 23 | dest: string 24 | kind: SlotKind 25 | 26 | proc newArgFiller*(): ref ArgFiller = 27 | new(result) 28 | result.counts = newCountTable[SlotKind]() 29 | 30 | using 31 | filler: ref ArgFiller 32 | 33 | proc required*(filler; argname: string, nargs = 1) = 34 | filler.slots.add(Slot(kind: Required, name: argname, nargs: nargs)) 35 | filler.counts.inc(Required, nargs) 36 | 37 | proc optional*(filler; argname: string) = 38 | filler.slots.add(Slot(kind: Optional, name: argname)) 39 | filler.counts.inc(Optional) 40 | 41 | proc wildcard*(filler; argname: string) = 42 | if filler.counts[Wildcard] > 0: 43 | raise ValueError.newException("More than one wildcard argument not allowed") 44 | filler.slots.add(Slot(kind: Wildcard, name: argname)) 45 | filler.counts.inc(Wildcard) 46 | 47 | proc minArgs*(filler): int = 48 | for slot in filler.slots: 49 | if slot.kind == Required: 50 | result.inc(slot.nargs) 51 | 52 | proc numArgsAfterWildcard*(filler): int = 53 | var afterWildcard = false 54 | for slot in filler.slots: 55 | if slot.kind == Wildcard: 56 | afterWildcard = true 57 | elif afterWildcard: 58 | case slot.kind 59 | of Required: 60 | result.inc(slot.nargs) 61 | of Optional: 62 | result.inc(1) 63 | of Wildcard: 64 | discard 65 | 66 | proc hasVariableArgs*(filler): bool = 67 | filler.counts[Optional] > 0 or filler.counts[Wildcard] > 0 68 | 69 | proc hasWildcard*(filler): bool = 70 | filler.counts[Wildcard] > 0 71 | 72 | proc upperBreakpoint*(filler): int = 73 | filler.counts[Required] + filler.counts[Optional] + filler.counts[Wildcard] 74 | 75 | proc channels*(filler; nargs: int): seq[FillChannel] = 76 | ## Given the number of arguments, show where those arguments will go 77 | var toget = newCountTable[SlotKind]() 78 | var left = nargs 79 | for kind in [Required, Optional, Wildcard]: 80 | var kind_left = filler.counts[kind] 81 | let totake = min(kind_left, left) 82 | if totake > 0: 83 | left.dec(totake) 84 | kind_left.dec(totake) 85 | toget.inc(kind, totake) 86 | var idx = 0 87 | for slot in filler.slots: 88 | if toget[slot.kind] > 0: 89 | case slot.kind 90 | of Required: 91 | result.add (idx..(idx+slot.nargs - 1), slot.name, slot.kind) 92 | of Optional: 93 | result.add (idx..idx, slot.name, slot.kind) 94 | of Wildcard: 95 | result.add (idx..(idx + left), slot.name, slot.kind) 96 | {.push assertions: off.} 97 | toget[slot.kind] = max(toget[slot.kind] - result[^1][0].len, 0) 98 | {.pop.} 99 | idx.inc(result[^1][0].len) 100 | 101 | proc missing*(filler; nargs: int): seq[string] = 102 | ## Given the number of arguments, which required arguments will 103 | ## not get a value? 104 | var left = nargs 105 | for slot in filler.slots: 106 | if slot.kind == Required: 107 | for c in 0.. 0: 59 | var n = stack.pop() 60 | for child in n.children: 61 | if child.kind == nnkIdent and child.strVal == name: 62 | return InsertionPoint(parent:n, child:child) 63 | else: 64 | stack.add(child) 65 | error("node not found: " & name) 66 | 67 | proc parentOf*(node: NimNode, child:NimNode): InsertionPoint = 68 | ## Recursively search for an ident node of the given name and return 69 | ## the parent of that node. 70 | var stack:seq[NimNode] = @[node] 71 | while stack.len > 0: 72 | var n = stack.pop() 73 | for c in n.children: 74 | if c == child: 75 | return InsertionPoint(parent:n, child:c) 76 | else: 77 | stack.add(c) 78 | error("node not found: " & child.repr) 79 | 80 | proc getInsertionPoint*(node: var NimNode, name:string): InsertionPoint = 81 | ## Return a node pair that you can replace with something else 82 | return node.parentOf(name) 83 | 84 | proc clear*(point: InsertionPoint):int = 85 | var i = 0 86 | for child in point.parent.children: 87 | if child == point.child: 88 | break 89 | inc(i) 90 | point.parent.del(i, 1) 91 | result = i 92 | 93 | proc replace*(point: InsertionPoint, newnode: NimNode) = 94 | ## Replace the child 95 | let i = point.clear() 96 | point.parent.insert(i, newnode) 97 | 98 | proc newObjectTypeDef*(name: string, isref:bool = false): UnfinishedObjectTypeDef {.compileTime.} = 99 | ## Creates: 100 | ## root -> 101 | ## type 102 | ## {name} = object 103 | ## insertion -> ... 104 | ## 105 | var insertion = newNimNode(nnkRecList) 106 | var objectty = nnkObjectTy.newTree( 107 | newEmptyNode(), 108 | newEmptyNode(), 109 | insertion, 110 | ) 111 | if isref: 112 | objectty = nnkRefTy.newTree(objectty) 113 | var root = newNimNode(nnkTypeSection).add( 114 | newNimNode(nnkTypeDef).add( 115 | ident(name), 116 | newEmptyNode(), 117 | objectty 118 | ) 119 | ) 120 | result = UnfinishedObjectTypeDef(root: root, insertion: insertion) 121 | 122 | proc addObjectField*(objtypedef: UnfinishedObjectTypeDef, name: string, kind: NimNode) {.compileTime.} = 123 | ## Adds a field to an object definition created by newObjectTypeDef 124 | objtypedef.insertion.add(newIdentDefs( 125 | newNimNode(nnkPostfix).add( 126 | ident("*"), 127 | ident(name), 128 | ), 129 | kind, 130 | newEmptyNode(), 131 | )) 132 | 133 | proc addObjectField*(objtypedef: UnfinishedObjectTypeDef, name: string, kind: string, isref: bool = false) {.compileTime.} = 134 | ## Adds a field to an object definition created by newObjectTypeDef 135 | if isref: 136 | addObjectField(objtypedef, name, nnkRefTy.newTree(ident(kind))) 137 | else: 138 | addObjectField(objtypedef, name, ident(kind)) 139 | 140 | #-------------------------------------------------------------- 141 | # case statements 142 | #-------------------------------------------------------------- 143 | proc newCaseStatement*(key: NimNode): ref UnfinishedCase = 144 | ## Create a new, unfinished case statement. Call `finalize` to finish it. 145 | ## 146 | ## case(`key`) 147 | new(result) 148 | result.root = nnkCaseStmt.newTree(key) 149 | 150 | proc newCaseStatement*(key: string): ref UnfinishedCase = 151 | return newCaseStatement(ident(key)) 152 | 153 | proc add*(n: ref UnfinishedCase, opt: seq[NimNode], body: NimNode) = 154 | ## Adds a branch to an UnfinishedCase 155 | ## 156 | ## Usage: 157 | ## var c = newCaseStatement("foo") 158 | ## c.add(@[newLit("apple"), newLit("banana")], quote do: 159 | ## echo "apple or banana" 160 | ## ) 161 | var branch = nnkOfBranch.newTree() 162 | for node in opt: 163 | branch.add(node) 164 | branch.add(body) 165 | n.cases.add(branch) 166 | 167 | proc add*(n: ref UnfinishedCase, opt: NimNode, body: NimNode) = 168 | ## Adds a branch to an UnfinishedCase 169 | n.add(@[opt], body) 170 | 171 | proc add*(n: ref UnfinishedCase, opt:string, body: NimNode) = 172 | ## Adds a branch to an UnfinishedCase 173 | ## 174 | ## c.add("foo", quote do: 175 | ## echo "value was foo" 176 | ## ) 177 | n.add(@[newStrLitNode(opt)], body) 178 | 179 | proc add*(n: ref UnfinishedCase, opts: seq[string], body: NimNode) = 180 | ## Adds a branch to an UnfinishedCase 181 | ## 182 | ## c.add(@["foo", "foo-also"], quote do: 183 | ## echo "value was foo" 184 | ## ) 185 | n.add(opts.mapIt(newStrLitNode(it)), body) 186 | 187 | proc add*(n: ref UnfinishedCase, opt:int, body: NimNode) = 188 | ## Adds an integer branch to an UnfinishedCase 189 | add(n, @[newLit(opt)], body) 190 | 191 | proc hasElse*(n: ref UnfinishedCase): bool = 192 | not n.elsebody.isNil 193 | 194 | proc addElse*(n: ref UnfinishedCase, body: NimNode) = 195 | ## Add an else: to an UnfinishedCase 196 | n.elsebody = body 197 | 198 | proc isValid*(n: ref UnfinishedCase): bool = 199 | return n.cases.len > 0 or n.elsebody != nil 200 | 201 | proc finalize*(n: ref UnfinishedCase): NimNode = 202 | if n.cases.len > 0: 203 | for branch in n.cases: 204 | n.root.add(branch) 205 | if n.elsebody != nil: 206 | n.root.add(nnkElse.newTree(n.elsebody)) 207 | result = n.root 208 | else: 209 | result = n.elsebody 210 | 211 | #-------------------------------------------------------------- 212 | # if statements 213 | #-------------------------------------------------------------- 214 | 215 | proc newIfStatement*(): ref UnfinishedIf = 216 | ## Create an unfinished if statement. 217 | new(result) 218 | result.root = nnkIfStmt.newTree() 219 | 220 | proc add*(n: ref UnfinishedIf, cond: NimNode, body: NimNode) = 221 | ## Add a branch to an if statement 222 | ## 223 | ## var f = newIfStatement() 224 | ## f.add() 225 | add(n.root, nnkElifBranch.newTree( 226 | cond, 227 | body, 228 | )) 229 | 230 | proc addElse*(n: ref UnfinishedIf, body: NimNode) = 231 | ## Add an else: to an UnfinishedIf 232 | n.elsebody = body 233 | 234 | proc isValid*(n: ref UnfinishedIf): bool = 235 | return n.root.len > 0 or n.elsebody != nil 236 | 237 | proc finalize*(n: ref UnfinishedIf): NimNode = 238 | ## Finish an If statement 239 | result = n.root 240 | if n.root.len == 0: 241 | # This "if" is only an "else" 242 | result = n.elsebody 243 | elif n.elsebody != nil: 244 | result.add(nnkElse.newTree(n.elsebody)) 245 | 246 | proc nimRepr*(n:NimNode): string = 247 | case n.kind 248 | of nnkStmtList: 249 | var lines:seq[string] 250 | for child in n: 251 | lines.add(child.nimRepr) 252 | result = lines.join("\L") 253 | of nnkCommand: 254 | let name = n[0].nimRepr 255 | var args:seq[string] 256 | for i, child in n: 257 | if i == 0: 258 | continue 259 | args.add(child.nimRepr) 260 | echo n.lispRepr 261 | let arglist = args.join(", ") 262 | result = &"{name}({arglist})" 263 | of nnkIdent: 264 | result = n.strVal 265 | of nnkStrLit: 266 | result = "[" & n.strVal & "]" 267 | else: 268 | result = &"" 269 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | _tmp 2 | -------------------------------------------------------------------------------- /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /tests/test1.nim: -------------------------------------------------------------------------------- 1 | import argparse 2 | import macros 3 | import os 4 | import osproc 5 | import sequtils 6 | import streams 7 | import strformat 8 | import strutils 9 | import unittest 10 | 11 | proc shlex(x:string):seq[string] = 12 | # XXX this is not accurate, but okay enough for testing 13 | if x == "": 14 | result = @[] 15 | else: 16 | result = x.split({' '}) 17 | 18 | template withEnv(name:string, value:string, body:untyped):untyped = 19 | let old_value = getEnv(name, "") 20 | putEnv(name, value) 21 | body 22 | putEnv(name, old_value) 23 | 24 | suite "flags": 25 | test "short flags": 26 | var p = newParser("some name"): 27 | flag("-a") 28 | flag("-b") 29 | 30 | check p.parse(shlex"-a").a == true 31 | check p.parse(shlex"-a").b == false 32 | check "some name" in p.help 33 | check "-a" in p.help 34 | check "-b" in p.help 35 | 36 | test "long flags": 37 | var p = newParser "some name": 38 | flag("--apple") 39 | flag("--banana") 40 | 41 | check p.parse(shlex"--apple").apple == true 42 | check p.parse(shlex"--apple").banana == false 43 | check p.parse(shlex"--banana").banana == true 44 | check "some name" in p.help 45 | check "--apple" in p.help 46 | check "--banana" in p.help 47 | 48 | test "short and long flags": 49 | var p = newParser "some name": 50 | flag("-a", "--apple") 51 | flag("--banana", "-b") 52 | 53 | check p.parse(shlex"--apple").apple == true 54 | check p.parse(shlex"--apple").banana == false 55 | check p.parse(shlex"-b").banana == true 56 | check "some name" in p.help 57 | check "--apple" in p.help 58 | check "-a" in p.help 59 | check "--banana" in p.help 60 | check "-b" in p.help 61 | 62 | test "multiple flags": 63 | var p = newParser: 64 | flag("-b", multiple=true) 65 | 66 | check p.parse(shlex("-b")).b == 1 67 | check p.parse(shlex("-b -b")).b == 2 68 | check p.parse(shlex("")).b == 0 69 | 70 | test "help text": 71 | var p = newParser: 72 | flag("-a", "--apple", help="Some apples") 73 | flag("--banana-split-and-ice-cream", help="More help") 74 | flag("-c", multiple=true) 75 | 76 | check "Some apples" in p.help 77 | check "More help" in p.help 78 | 79 | test "unknown flag": 80 | var p = newParser: 81 | flag("-a") 82 | expect UsageError: 83 | discard p.parse(shlex"-b") 84 | 85 | test "shortcircuit": 86 | var p = newParser: 87 | flag("-V", "--version", shortcircuit=true) 88 | 89 | try: 90 | p.run(shlex"--version") 91 | assert false, "Should not get here" 92 | except ShortCircuit as e: 93 | check e.flag == "version" 94 | 95 | try: 96 | discard p.parse(shlex"-V") 97 | assert false, "Should not get here" 98 | except ShortCircuit as e: 99 | check e.flag == "version" 100 | 101 | 102 | suite "options": 103 | test "short options": 104 | var p = newParser: 105 | option("-a", help="Stuff") 106 | check p.parse(shlex"-a=5").a == "5" 107 | check p.parse(shlex"-a 5").a == "5" 108 | check p.parse(shlex"-a 5").a_opt.get() == "5" 109 | check p.parse(shlex"").a_opt.isNone 110 | 111 | check "Stuff" in p.help 112 | 113 | test "long options": 114 | var p = newParser: 115 | option("--apple") 116 | check p.parse(shlex"--apple=10").apple == "10" 117 | check p.parse(shlex"--apple 10").apple == "10" 118 | check p.parse(shlex"--apple 10").apple_opt.get() == "10" 119 | check p.parse(shlex"").apple_opt.isNone 120 | 121 | test "option default": 122 | var p = newParser: 123 | option("--category", default=some("pinball")) 124 | check p.parse(shlex"").category == "pinball" 125 | check p.parse(shlex"--category foo").category == "foo" 126 | check p.parse(shlex"").category_opt.get() == "pinball" 127 | check p.parse(shlex"--category foo").category_opt.get() == "foo" 128 | 129 | test "option default from env var": 130 | var p = newParser: 131 | option("--category", env="HELLO", default=some("who")) 132 | check "HELLO" in p.help 133 | check p.parse(shlex"").category == "who" 134 | check p.parse(shlex"").category_opt.get() == "who" 135 | withEnv("HELLO", "Adele"): 136 | check p.parse(shlex"").category == "Adele" 137 | check p.parse(shlex"").category_opt.get() == "Adele" 138 | check p.parse(shlex"--category hey").category == "hey" 139 | check p.parse(shlex"--category hey").category_opt.get() == "hey" 140 | 141 | test "unknown option": 142 | var p = newParser: 143 | option("-a") 144 | expect UsageError: 145 | discard p.parse(shlex"-b") 146 | 147 | test "multiple options on non-multi option": 148 | var p = newParser: 149 | option("-a") 150 | option("-b", default = some("something")) 151 | expect UsageError: 152 | discard p.parse(shlex"-a 10 -a 20") 153 | expect UsageError: 154 | discard p.parse(shlex"-b hey -b ho") 155 | check p.parse(shlex"-b foo").b == "foo" 156 | 157 | test "multiple options": 158 | var p = newParser: 159 | option("-a", multiple=true) 160 | check p.parse(shlex"-a 10 -a 20").a == @["10", "20"] 161 | check p.parse(shlex"").a == [] 162 | check p.parse(shlex"-a 20").a == @["20"] 163 | 164 | test "choices": 165 | var p = newParser: 166 | option("-b", choices = @["first", "second", "third"]) 167 | 168 | check p.parse(shlex"-b first").b == "first" 169 | expect UsageError: 170 | discard p.parse(shlex"-b unknown") 171 | 172 | test "choices multiple": 173 | var p = newParser: 174 | option("-b", multiple=true, choices = @["first", "second", "third"]) 175 | 176 | check p.parse(shlex"-b first").b == @["first"] 177 | check p.parse(shlex"-b first -b second").b == @["first", "second"] 178 | 179 | test "option with - value argument": 180 | var p = newParser: 181 | option("-b") 182 | check p.parse(shlex"-b -").b == "-" 183 | check p.parse(shlex"-b -a").b == "-a" 184 | 185 | test "required options": 186 | var p = newParser: 187 | option("-b", "--bob", required = true) 188 | expect UsageError: 189 | discard p.parse(@[]) 190 | check p.parse(shlex"-b foo").bob == "foo" 191 | check p.parse(shlex"-b foo").bob_opt.get() == "foo" 192 | check p.parse(@["-b", ""]).bob == "" 193 | check p.parse(@["-b", ""]).bob_opt.get() == "" 194 | 195 | test "required options still allow for --help": 196 | var p = newParser: 197 | help("Top level help") 198 | option("-b", required=true) 199 | expect ShortCircuit: 200 | discard p.parse(shlex"--help", quitOnHelp=false) 201 | 202 | test "required options provided by env": 203 | var p = newParser: 204 | option("-b", "--bob", env="BOB", required = true) 205 | withEnv("BOB", "something"): 206 | check p.parse(shlex"").bob == "something" 207 | check p.parse(shlex"--bob another").bob == "another" 208 | 209 | suite "args": 210 | test "single, required arg": 211 | var p = newParser: 212 | arg("name") 213 | check p.parse(shlex"foo").name == "foo" 214 | check "name" in p.help 215 | 216 | test "args are required": 217 | var p = newParser: 218 | arg("name") 219 | expect UsageError: 220 | discard p.parse(shlex"") 221 | 222 | test "args with hyphens become underscores": 223 | var p = newParser: 224 | arg("some-arg") 225 | check p.parse(shlex"something").some_arg == "something" 226 | 227 | test "extra args is an error": 228 | var p = newParser: 229 | arg("only") 230 | expect UsageError: 231 | discard p.parse(shlex"one two") 232 | 233 | test "single arg with default": 234 | var p = newParser: 235 | arg("name", default=some("foo")) 236 | check p.parse(shlex"").name == "foo" 237 | check p.parse(shlex"something").name == "something" 238 | 239 | test "single arg with env default": 240 | var p = newParser: 241 | arg("name", env="SOMETHING", default=some("foo")) 242 | check "SOMETHING" in p.help 243 | check p.parse(shlex"").name == "foo" 244 | check p.parse(shlex"something").name == "something" 245 | withEnv("SOMETHING", "goober"): 246 | check p.parse(shlex"").name == "goober" 247 | check p.parse(shlex"something").name == "something" 248 | 249 | test "2 args": 250 | var p = newParser: 251 | arg("name") 252 | arg("age") 253 | check p.parse(shlex"foo bar").name == "foo" 254 | check p.parse(shlex"foo bar").age == "bar" 255 | check "name" in p.help 256 | check "age" in p.help 257 | 258 | test "arg help": 259 | var p = newParser: 260 | arg("name", help="Something") 261 | check "Something" in p.help 262 | 263 | test "optional required optional required optional wildcard": 264 | var p = newParser: 265 | arg("a", default=some("bob")) 266 | arg("b") 267 | arg("c", default=some("sam")) 268 | arg("d", nargs = 2) 269 | arg("e", default=some("al")) 270 | arg("w", nargs = -1) 271 | var r = p.parse(shlex"1 2 3") 272 | check r.a == "bob" 273 | check r.b == "1" 274 | check r.c == "sam" 275 | check r.d == @["2", "3"] 276 | check r.e == "al" 277 | check r.w.len == 0 278 | r = p.parse(shlex"1 2 3 4") 279 | check r.a == "1" 280 | check r.b == "2" 281 | check r.c == "sam" 282 | check r.d == @["3", "4"] 283 | check r.e == "al" 284 | check r.w.len == 0 285 | r = p.parse(shlex"1 2 3 4 5") 286 | check r.a == "1" 287 | check r.b == "2" 288 | check r.c == "3" 289 | check r.d == @["4", "5"] 290 | check r.e == "al" 291 | check r.w.len == 0 292 | r = p.parse(shlex"1 2 3 4 5 6") 293 | check r.a == "1" 294 | check r.b == "2" 295 | check r.c == "3" 296 | check r.d == @["4", "5"] 297 | check r.e == "6" 298 | check r.w.len == 0 299 | r = p.parse(shlex"1 2 3 4 5 6 7") 300 | check r.a == "1" 301 | check r.b == "2" 302 | check r.c == "3" 303 | check r.d == @["4", "5"] 304 | check r.e == "6" 305 | check r.w == @["7"] 306 | r = p.parse(shlex"1 2 3 4 5 6 7 8") 307 | check r.a == "1" 308 | check r.b == "2" 309 | check r.c == "3" 310 | check r.d == @["4", "5"] 311 | check r.e == "6" 312 | check r.w == @["7", "8"] 313 | 314 | test "r o w o r": 315 | var p = newParser: 316 | arg("a") 317 | arg("b", default = some("hey")) 318 | arg("c", nargs = -1) 319 | arg("d", default = some("sam")) 320 | arg("e", nargs = 2) 321 | var r = p.parse(shlex"1 2 3") 322 | check r.a == "1" 323 | check r.b == "hey" 324 | check r.c.len == 0 325 | check r.d == "sam" 326 | check r.e == @["2", "3"] 327 | r = p.parse(shlex"1 2 3 4") 328 | check r.a == "1" 329 | check r.b == "2" 330 | check r.c.len == 0 331 | check r.d == "sam" 332 | check r.e == @["3", "4"] 333 | r = p.parse(shlex"1 2 3 4 5") 334 | check r.a == "1" 335 | check r.b == "2" 336 | check r.c.len == 0 337 | check r.d == "3" 338 | check r.e == @["4", "5"] 339 | r = p.parse(shlex"1 2 3 4 5 6") 340 | check r.a == "1" 341 | check r.b == "2" 342 | check r.c == @["3"] 343 | check r.d == "4" 344 | check r.e == @["5", "6"] 345 | r = p.parse(shlex"1 2 3 4 5 6 7") 346 | check r.a == "1" 347 | check r.b == "2" 348 | check r.c == @["3", "4"] 349 | check r.d == "5" 350 | check r.e == @["6", "7"] 351 | 352 | test "nargs=2": 353 | var p = newParser: 354 | arg("name", nargs=2) 355 | check p.parse(shlex"a b").name == @["a", "b"] 356 | 357 | test "nargs=-1": 358 | var p = newParser: 359 | arg("thing", nargs = -1) 360 | check p.parse(shlex"").thing.len == 0 361 | check p.parse(shlex"a").thing == @["a"] 362 | check p.parse(shlex"a b c").thing == @["a", "b", "c"] 363 | 364 | test "nargs=-1 at the end": 365 | var p = newParser: 366 | arg("first") 367 | arg("thing", nargs = -1) 368 | check p.parse(shlex"first").thing.len == 0 369 | check p.parse(shlex"first a").thing == @["a"] 370 | check p.parse(shlex"first a b c").thing == @["a", "b", "c"] 371 | 372 | test "nargs=-1 in the middle": 373 | var p = newParser: 374 | arg("first") 375 | arg("thing", nargs = -1) 376 | arg("last") 377 | check p.parse(shlex"first last").thing.len == 0 378 | check p.parse(shlex"first a last").thing == @["a"] 379 | check p.parse(shlex"first a b c last").thing == @["a", "b", "c"] 380 | 381 | test "nargs=-1 at the beginning": 382 | var p = newParser: 383 | flag("-a") 384 | arg("thing", nargs = -1) 385 | arg("last", nargs = 2) 386 | check p.parse(shlex"last 2").thing.len == 0 387 | check p.parse(shlex"a last 2").thing == @["a"] 388 | check p.parse(shlex"a b c last 2").thing == @["a", "b", "c"] 389 | check p.parse(shlex"last 2").last == @["last", "2"] 390 | 391 | test "nargs=-1 nargs=2 nargs=2": 392 | var p = newParser: 393 | arg("first", nargs = -1) 394 | arg("middle", nargs = 2) 395 | arg("last", nargs = 2) 396 | check p.parse(shlex"a b c d").first.len == 0 397 | check p.parse(shlex"a b c d").middle == @["a", "b"] 398 | check p.parse(shlex"a b c d").last == @["c", "d"] 399 | check p.parse(shlex"a b c d e").first == @["a"] 400 | check p.parse(shlex"a b c d e").middle == @["b", "c"] 401 | check p.parse(shlex"a b c d e").last == @["d", "e"] 402 | 403 | test "nargs=-1 nargs=1 w/ default": 404 | var p = newParser: 405 | arg("first", nargs = -1) 406 | arg("last", default=some("hey")) 407 | check p.parse(shlex"").last == "hey" 408 | check p.parse(shlex"hoo").last == "hoo" 409 | check p.parse(shlex"a b goo").last == "goo" 410 | 411 | test "extra args": 412 | var p = newParser: 413 | arg("first") 414 | arg("extra", nargs = -1) 415 | let res = p.parse(shlex"a -b c -foo -d -e=goo app app") 416 | check res.first == "a" 417 | check res.extra == @["-b", "c", "-foo", "-d", "-e=goo", "app", "app"] 418 | 419 | test "-- extra args": 420 | var p = newParser: 421 | flag("-a", "--apple") 422 | option("-b", "--banana") 423 | arg("hi") 424 | arg("extra", nargs = -1) 425 | let res = p.parse(shlex("-a -b foo hi -- -a --apple -b --banana goofy glop")) 426 | check res.apple == true 427 | check res.banana == "foo" 428 | check res.hi == "hi" 429 | check res.extra == @["-a", "--apple", "-b", "--banana", "goofy", "glop"] 430 | 431 | suite "autohelp": 432 | test "static prog name": 433 | var p = newParser("staticname"): 434 | help("{prog}") 435 | check "staticname" in p.help 436 | 437 | test "dynamic prog name": 438 | var p = newParser: 439 | help("{prog}") 440 | check getAppFilename().extractFilename() in p.help 441 | 442 | test "helpbydefault": 443 | var res:string 444 | var p = newParser: 445 | help("Top level help") 446 | flag("--foo") 447 | run: 448 | res.add("main ran") 449 | command("something"): 450 | help("sub help") 451 | flag("--bar") 452 | run: 453 | res.add("sub ran") 454 | 455 | var op = newStringStream("") 456 | echo "about to run p.run" 457 | p.run(shlex"-h", quitOnHelp = false, output = op) 458 | op.setPosition(0) 459 | var output = op.readAll() 460 | check "--foo" in output 461 | check "Top level help" in output 462 | check res == "" 463 | 464 | op = newStringStream("") 465 | p.run(shlex"something --help", quitOnHelp = false, output = op) 466 | op.setPosition(0) 467 | output = op.readAll() 468 | check "--bar" in output 469 | check "sub help" in output 470 | check res == "" 471 | 472 | test "nohelpflag": 473 | var res:string 474 | var p = newParser: 475 | nohelpflag() 476 | run: 477 | res.add("main ran") 478 | command("something"): 479 | nohelpflag() 480 | run: 481 | res.add("sub ran") 482 | 483 | expect UsageError: 484 | p.run(shlex"-h", quitOnHelp = false) 485 | 486 | expect UsageError: 487 | p.run(shlex"something --help", quitOnHelp = false) 488 | 489 | test "parse help": 490 | let 491 | p = newParser: discard 492 | expect ShortCircuit: 493 | try: 494 | discard p.parse(@["-h"]) 495 | except ShortCircuit as e: 496 | check e.flag == "argparse_help" 497 | raise e 498 | 499 | test "subcommand help": 500 | var p = newParser: 501 | command "foo": 502 | help("foo sub help") 503 | flag("-a") 504 | command "bar": 505 | help("bar sub help") 506 | flag("-b") 507 | var found_help: string 508 | try: 509 | var opts = p.parse(@["foo", "--help"]) 510 | except ShortCircuit as e: 511 | if e.flag == "argparse_help": 512 | found_help = e.help 513 | check "foo sub help" in found_help 514 | check "bar sub help" notin found_help 515 | 516 | suite "commands": 517 | test "run": 518 | var res:string = "hello" 519 | 520 | var p = newParser: 521 | command "command1": 522 | help("Some help text") 523 | flag("-a") 524 | run: 525 | res = $opts.a 526 | 527 | p.run(shlex"command1 -a") 528 | check res == "true" 529 | p.run(shlex"command1") 530 | check res == "false" 531 | 532 | test "run only one": 533 | var res:string 534 | 535 | var p = newParser: 536 | command "command1": 537 | run: 538 | res.add("command1") 539 | run: 540 | if opts.argparse_command == "": 541 | res.add("root run") 542 | p.run(shlex"command1") 543 | check res == "command1" 544 | res.setLen(0) 545 | 546 | p.run(@[]) 547 | check res == "root run" 548 | 549 | test "run order": 550 | var res:string 551 | var p = newParser: 552 | run: res.add("a") 553 | command "sub": 554 | run: res.add("b") 555 | command "sub2": 556 | run: res.add("c") 557 | p.run(shlex"sub sub2") 558 | check res == "abc" 559 | 560 | test "two commands": 561 | var res:string = "" 562 | 563 | var p = newParser: 564 | command "move": 565 | arg("howmuch") 566 | run: 567 | res = "moving " & opts.howmuch 568 | command "eat": 569 | arg("what") 570 | run: 571 | res = "you ate " & opts.what 572 | 573 | p.run(shlex"move 10") 574 | check res == "moving 10" 575 | p.run(shlex"eat apple") 576 | check res == "you ate apple" 577 | 578 | test "access parent": 579 | var res:string = "" 580 | var p = newParser: 581 | option("-a") 582 | command "sub": 583 | option("-a") 584 | run: 585 | res = &"{opts.parentOpts.a},{opts.a}" 586 | p.run(shlex"-a parent sub -a child") 587 | check res == "parent,child" 588 | 589 | test "unknown command": 590 | var res:string = "" 591 | 592 | var p = newParser: 593 | command "sub": 594 | option("-a") 595 | run: 596 | res = "did run" 597 | expect UsageError: 598 | discard p.parse(shlex"madeupcommand") 599 | 600 | check res == "" 601 | 602 | test "command groups": 603 | var p = newParser: 604 | command("first", group = "groupA"): 605 | help "A first command" 606 | run: discard 607 | command("second", group = "groupB"): 608 | help "A second command" 609 | run: discard 610 | command("third", group="groupA"): 611 | help "A third command" 612 | run: discard 613 | 614 | check "groupA" in p.help 615 | check "groupB" in p.help 616 | echo p.help 617 | 618 | test "command group ordering": 619 | var p = newParser: 620 | command("a", group = "AA"): discard 621 | command("b", group = "BB"): discard 622 | command("c", group = "CC"): discard 623 | command("d", group = "DD"): discard 624 | command("e", group = "EE"): discard 625 | command("f", group = "FF"): discard 626 | let indexes = @["AA","BB","CC","DD","EE","FF"].mapIt(p.help.find(it)) 627 | check indexes == indexes.sorted() 628 | 629 | test "sub sub sub": 630 | var res:string = "" 631 | var p = newParser: 632 | command("a"): 633 | command("b"): 634 | command("c"): 635 | run: res.add "hi from c" 636 | p.run(shlex"a b c") 637 | check res == "hi from c" 638 | 639 | res.setLen(0) 640 | p.run(shlex"a b") 641 | check res == "" 642 | 643 | test "arg and command": 644 | var res:string = "" 645 | var p = newParser: 646 | arg("something") 647 | command("a"): 648 | run: 649 | res.add opts.parentOpts.something 650 | p.run(shlex"hello a") 651 | check res == "hello" 652 | res.setLen(0) 653 | 654 | p.run(shlex"a a") 655 | check res == "a" 656 | 657 | test "blank default": 658 | var res: string 659 | var p = newParser: 660 | command "cat": 661 | arg("version", default = some("")) 662 | run: 663 | res.add(opts.version) 664 | p.run(shlex"cat") 665 | check res == "" 666 | p.run(shlex"cat foo") 667 | check res == "foo" 668 | 669 | test "same parser name": 670 | ## Parsers with the same name are allowed (and made unique) 671 | var p1 = newParser("jim"): 672 | flag("-a") 673 | var p2 = newParser("jim"): 674 | flag("-b") 675 | 676 | let r1 = p1.parse(shlex"-a") 677 | check r1.a == true 678 | let r2 = p2.parse(shlex"-b") 679 | check r2.b == true 680 | 681 | test "parse access to sub-opts": 682 | var p = newParser: 683 | command "foo": 684 | flag("-a") 685 | command "bar": 686 | flag("-b") 687 | let o1 = p.parse(shlex"foo -a") 688 | check o1.argparse_command == "foo" # conflict-unlikely version 689 | check o1.command == "foo" # shortcut version 690 | check o1.argparse_foo_opts.isSome # conflict-unlikely version 691 | check o1.foo.isSome # shortcut version 692 | check o1.foo.get.a == true 693 | 694 | check o1.argparse_bar_opts.isNone # conflict-unlikely version 695 | check o1.bar.isNone 696 | 697 | test "parse sub-opts name conflicts": 698 | var p = newParser: 699 | flag("--foo") 700 | arg("command") # why would you do this? :) 701 | command "foo": 702 | flag("-a") 703 | let o1 = p.parse(shlex"hey foo -a") 704 | check o1.argparse_command == "foo" 705 | check o1.command == "hey" 706 | 707 | check o1.foo == false # --foo flag 708 | check o1.argparse_foo_opts.isSome # foo command 709 | check o1.argparse_foo_opts.get.a == true 710 | 711 | test "flag named command": 712 | var p = newParser: 713 | flag("--command") 714 | command "foo": 715 | discard 716 | let opts = p.parse(shlex"foo") 717 | check opts.command == false 718 | check opts.argparse_command == "foo" 719 | 720 | test "option named command": 721 | var p = newParser: 722 | option("--command") 723 | command "foo": 724 | discard 725 | let opts = p.parse(shlex"foo") 726 | check opts.command == "" 727 | check opts.argparse_command == "foo" 728 | 729 | test "arg named command": 730 | var p = newParser: 731 | arg("command") 732 | command "foo": 733 | discard 734 | let opts = p.parse(shlex"hey foo") 735 | check opts.command == "hey" 736 | check opts.argparse_command == "foo" 737 | 738 | test "flag/command name conflict": 739 | var p = newParser: 740 | flag("--foo") 741 | command "foo": discard 742 | let opts = p.parse(shlex"foo") 743 | check opts.command == "foo" 744 | check opts.foo == false 745 | check opts.argparse_foo_opts.isSome 746 | 747 | test "option/command name conflict": 748 | var p = newParser: 749 | option("--foo") 750 | command "foo": discard 751 | let opts = p.parse(shlex"foo") 752 | check opts.command == "foo" 753 | check opts.foo == "" 754 | check opts.argparse_foo_opts.isSome 755 | 756 | test "command with hyphen": 757 | var p = newParser: 758 | command "some-command": discard 759 | let opts = p.parse(shlex"some-command") 760 | check opts.command == "some-command" 761 | check opts.argparse_some_command_opts.isSome 762 | 763 | test "arg/command name conflict": 764 | var p = newParser: 765 | arg("foo") 766 | command "foo": discard 767 | let opts = p.parse(shlex"hey foo") 768 | check opts.command == "foo" 769 | check opts.foo == "hey" 770 | check opts.argparse_foo_opts.isSome 771 | 772 | 773 | suite "misc": 774 | test "README run": 775 | var res:seq[string] 776 | var p = newParser: 777 | flag("-a", "--apple") 778 | flag("-b", help="Show a banana") 779 | option("-o", "--output", help="Output to this file") 780 | command("somecommand"): 781 | arg("name") 782 | arg("others", nargs = -1) 783 | run: 784 | res.add opts.name 785 | res.add opts.others 786 | res.add $opts.parentOpts.apple 787 | res.add $opts.parentOpts.b 788 | res.add opts.parentOpts.output 789 | 790 | p.run(@["--apple", "-o=foo", "somecommand", "myname", "thing1", "thing2"]) 791 | check res == @[ 792 | "myname", 793 | "thing1", 794 | "thing2", 795 | "true", 796 | "false", 797 | "foo", 798 | ] 799 | 800 | test "README parse": 801 | var p = newParser: 802 | flag("-a", "--apple") 803 | flag("-b", help="Show a banana") 804 | option("-o", "--output", help="Output to this file") 805 | arg("name") 806 | arg("others", nargs = -1) 807 | 808 | var opts = p.parse(@["--apple", "-o=foo", "hi"]) 809 | assert opts.apple == true 810 | assert opts.b == false 811 | assert opts.output == "foo" 812 | assert opts.name == "hi" 813 | assert opts.others == @[] 814 | 815 | test "parse with no args": 816 | let tmpfile = currentSourcePath().parentDir() / "something.nim" 817 | defer: 818 | removeFile(tmpfile) 819 | removeFile(tmpfile.changeFileExt(ExeExt)) 820 | tmpfile.writeFile(""" 821 | import argparse 822 | var p = newParser: 823 | arg("name") 824 | 825 | echo p.parse().name 826 | """) 827 | let output = execProcess(findExe"nim", 828 | args = ["c", "--hints:off", "--verbosity:0", "-r", tmpfile, "bob"], 829 | options = {}) 830 | checkpoint "=============== output ==============" 831 | checkpoint output 832 | checkpoint "=====================================" 833 | check output == "bob\n" 834 | 835 | test "std/logging": 836 | let tmpfile = currentSourcePath().parentDir() / "std_logging.nim" 837 | defer: 838 | removeFile(tmpfile) 839 | removeFile(tmpfile.changeFileExt(ExeExt)) 840 | tmpfile.writeFile(""" 841 | import std/logging, argparse 842 | addHandler newConsoleLogger() 843 | error "ok" 844 | var p = newParser: 845 | arg("foo") 846 | """) 847 | let output = execProcess(findExe"nim", 848 | args = ["c", "--hints:off", "--verbosity:0", "-r", tmpfile], 849 | options = {poStdErrToStdOut}) 850 | checkpoint "=============== output ==============" 851 | checkpoint output 852 | checkpoint "=====================================" 853 | check "ERROR ok" in output 854 | -------------------------------------------------------------------------------- /tests/testBackend.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import argparse/backend 3 | 4 | suite "ParseState": 5 | 6 | test "basic": 7 | var state = newParseState(["-a", "hi"]) 8 | check state.cursor == 0 9 | check state.token.get() == "-a" 10 | check state.key.get() == "-a" 11 | check state.value.get() == "hi" 12 | 13 | test "= separator": 14 | var state = newParseState(["-a=foo"]) 15 | check state.token.get() == "-a=foo" 16 | check state.key.get() == "-a" 17 | check state.value.get() == "foo" 18 | 19 | test "- value": 20 | var state = newParseState(["-a=-b"]) 21 | check state.token.get() == "-a=-b" 22 | check state.key.get() == "-a" 23 | check state.value.get() == "-b" 24 | 25 | test "- value space": 26 | var state = newParseState(["-a", "-b"]) 27 | check state.token.get() == "-a" 28 | check state.key.get() == "-a" 29 | check state.value.get() == "-b" 30 | 31 | test "consume flag": 32 | var state = newParseState(["-a", "hi"]) 33 | state.consume(ArgFlag) 34 | check state.cursor == 1 35 | check state.token.get() == "hi" 36 | check state.key.isSome() 37 | check state.key.get() == "hi" 38 | check state.value.isNone() 39 | check state.extra.len == 0 40 | 41 | state.consume(ArgFlag) 42 | check state.cursor == 2 43 | check state.done == true 44 | check state.key.isNone() 45 | check state.value.isNone() 46 | check state.token.isNone() 47 | check state.extra.len == 0 48 | 49 | test "consume option": 50 | var state = newParseState(["-a", "hi"]) 51 | state.consume(ArgOption) 52 | check state.cursor == 2 53 | check state.done == true 54 | check state.key.isNone() 55 | check state.value.isNone() 56 | check state.token.isNone() 57 | check state.extra.len == 0 58 | 59 | test "consume arg": 60 | var state = newParseState(["-a", "hi"]) 61 | state.consume(ArgArgument) 62 | check state.cursor == 1 63 | check state.done == false 64 | check state.key.isSome() 65 | check state.value.isNone() 66 | check state.token.get() == "hi" 67 | check state.key.get() == "hi" 68 | check state.extra.len == 0 69 | 70 | test "skip flag": 71 | var state = newParseState(["-a", "hi"]) 72 | state.skip() 73 | check state.cursor == 1 74 | check state.extra == @["-a"] 75 | state.skip() 76 | check state.cursor == 2 77 | check state.extra == @["-a", "hi"] 78 | check state.done == true 79 | -------------------------------------------------------------------------------- /tests/testFiller.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import argparse/filler 3 | 4 | # R = required 5 | # Rn = required, width n 6 | # O = optional 7 | # W = wildcard 8 | 9 | proc ch(filler: ref ArgFiller, nargs: int): string = 10 | ## Get a string representation of how the args will be distributed 11 | for channel in filler.channels(nargs): 12 | for i in channel.idx: 13 | result.add(channel.dest) 14 | 15 | test "R": 16 | var filler = newArgFiller() 17 | filler.required("a") 18 | check filler.minArgs == 1 19 | check filler.ch(1) == "a" 20 | check filler.missing(0) == @["a"] 21 | 22 | test "R2": 23 | var filler = newArgFiller() 24 | filler.required("a", 2) 25 | check filler.minArgs == 2 26 | check filler.ch(2) == "aa" 27 | check filler.missing(0) == @["a", "a"] 28 | 29 | test "RR": 30 | var filler = newArgFiller() 31 | filler.required("a") 32 | filler.required("b") 33 | check filler.minArgs == 2 34 | check filler.ch(2) == "ab" 35 | check filler.missing(0) == @["a", "b"] 36 | 37 | test "O": 38 | var filler = newArgFiller() 39 | filler.optional("a") 40 | check filler.minArgs == 0 41 | check filler.ch(0) == "" 42 | check filler.ch(1) == "a" 43 | check filler.missing(0).len == 0 44 | 45 | test "OO": 46 | var filler = newArgFiller() 47 | filler.optional("a") 48 | filler.optional("b") 49 | check filler.minArgs == 0 50 | check filler.ch(0) == "" 51 | check filler.ch(1) == "a" 52 | check filler.ch(2) == "ab" 53 | check filler.missing(0).len == 0 54 | 55 | test "ROO": 56 | var filler = newArgFiller() 57 | filler.required("a") 58 | filler.optional("b") 59 | filler.optional("c") 60 | check filler.minArgs == 1 61 | check filler.ch(1) == "a" 62 | check filler.ch(2) == "ab" 63 | check filler.ch(3) == "abc" 64 | check filler.missing(0) == @["a"] 65 | 66 | test "ORO": 67 | var filler = newArgFiller() 68 | filler.optional("a") 69 | filler.required("b") 70 | filler.optional("c") 71 | check filler.minArgs == 1 72 | check filler.ch(1) == "b" 73 | check filler.ch(2) == "ab" 74 | check filler.ch(3) == "abc" 75 | check filler.missing(0) == @["b"] 76 | 77 | test "OORROO": 78 | var filler = newArgFiller() 79 | filler.optional("a") 80 | filler.optional("b") 81 | filler.required("c") 82 | filler.required("d") 83 | filler.optional("e") 84 | filler.optional("f") 85 | check filler.minArgs == 2 86 | check filler.ch(2) == "cd" 87 | check filler.ch(3) == "acd" 88 | check filler.ch(4) == "abcd" 89 | check filler.ch(5) == "abcde" 90 | check filler.ch(6) == "abcdef" 91 | check filler.missing(0) == @["c", "d"] 92 | check filler.missing(1) == @["d"] 93 | 94 | test "OOROROO": 95 | var filler = newArgFiller() 96 | filler.optional("a") 97 | filler.optional("b") 98 | filler.required("c") 99 | filler.optional("d") 100 | filler.required("e") 101 | filler.optional("f") 102 | filler.optional("g") 103 | check filler.minArgs == 2 104 | check filler.ch(2) == "ce" 105 | check filler.ch(3) == "ace" 106 | check filler.ch(4) == "abce" 107 | check filler.ch(5) == "abcde" 108 | check filler.ch(6) == "abcdef" 109 | check filler.ch(7) == "abcdefg" 110 | check filler.missing(0) == @["c", "e"] 111 | check filler.missing(1) == @["e"] 112 | 113 | test "OOR": 114 | var filler = newArgFiller() 115 | filler.optional("a") 116 | filler.optional("b") 117 | filler.required("c") 118 | check filler.minArgs == 1 119 | check filler.ch(1) == "c" 120 | check filler.ch(2) == "ac" 121 | check filler.ch(3) == "abc" 122 | check filler.missing(0) == @["c"] 123 | 124 | test "W": 125 | var filler = newArgFiller() 126 | filler.wildcard("a") 127 | check filler.minArgs == 0 128 | check filler.ch(0) == "" 129 | check filler.ch(1) == "a" 130 | check filler.ch(2) == "aa" 131 | check filler.ch(3) == "aaa" 132 | check filler.missing(0).len == 0 133 | 134 | test "WR": 135 | var filler = newArgFiller() 136 | filler.wildcard("a") 137 | filler.required("b") 138 | check filler.minArgs == 1 139 | check filler.ch(1) == "b" 140 | check filler.ch(2) == "ab" 141 | check filler.ch(3) == "aab" 142 | check filler.missing(0) == @["b"] 143 | 144 | test "RW": 145 | var filler = newArgFiller() 146 | filler.required("a") 147 | filler.wildcard("b") 148 | check filler.minArgs == 1 149 | check filler.ch(1) == "a" 150 | check filler.ch(2) == "ab" 151 | check filler.ch(3) == "abb" 152 | check filler.missing(0) == @["a"] 153 | 154 | test "RWR": 155 | var filler = newArgFiller() 156 | filler.required("a") 157 | filler.wildcard("b") 158 | filler.required("c") 159 | check filler.minArgs == 2 160 | check filler.ch(2) == "ac" 161 | check filler.ch(3) == "abc" 162 | check filler.ch(4) == "abbc" 163 | check filler.missing(0) == @["a", "c"] 164 | check filler.missing(1) == @["c"] 165 | 166 | test "WW": 167 | var filler = newArgFiller() 168 | filler.wildcard("a") 169 | expect Exception: 170 | filler.wildcard("b") 171 | 172 | test "WO": 173 | var filler = newArgFiller() 174 | filler.wildcard("a") 175 | filler.optional("b") 176 | check filler.minArgs == 0 177 | check filler.ch(0) == "" 178 | check filler.ch(1) == "b" 179 | check filler.ch(2) == "ab" 180 | check filler.ch(3) == "aab" 181 | 182 | test "OW": 183 | var filler = newArgFiller() 184 | filler.optional("a") 185 | filler.wildcard("b") 186 | check filler.minArgs == 0 187 | check filler.ch(0) == "" 188 | check filler.ch(1) == "a" 189 | check filler.ch(2) == "ab" 190 | check filler.ch(3) == "abb" 191 | 192 | test "ROWOR": 193 | var filler = newArgFiller() 194 | filler.required("a") 195 | filler.optional("b") 196 | filler.wildcard("c") 197 | filler.optional("d") 198 | filler.required("e") 199 | check filler.minArgs == 2 200 | check filler.ch(2) == "ae" 201 | check filler.ch(3) == "abe" 202 | check filler.ch(4) == "abde" 203 | check filler.ch(5) == "abcde" 204 | check filler.ch(6) == "abccde" --------------------------------------------------------------------------------