pax_global_header00006660000000000000000000000064134026036020014506gustar00rootroot0000000000000052 comment=65da8739a7534b8d78249a90778e1b38d4bdcf5c docopt.nim-0.6.8/000077500000000000000000000000001340260360200135735ustar00rootroot00000000000000docopt.nim-0.6.8/.gitignore000066400000000000000000000002041340260360200155570ustar00rootroot00000000000000* !.gitignore !*.nim /*.nim !*.nimble !*.docopt !*.md !nim.cfg !README* !LICENSE !/src !/src/docopt !/test !/examples !/.travis.yml docopt.nim-0.6.8/.travis.yml000066400000000000000000000016731340260360200157130ustar00rootroot00000000000000language: c env: - nim_branch=master - nim_branch=devel matrix: allow_failures: - env: nim_branch=devel fast_finish: true install: - | if [ ! -x nim-$nim_branch/bin/nim ]; then git clone -b $nim_branch --depth 1 git://github.com/nim-lang/nim nim-$nim_branch/ cd nim-$nim_branch git clone --depth 1 git://github.com/nim-lang/csources csources/ cd csources sh build.sh cd .. rm -rf csources bin/nim c koch ./koch boot -d:release ./koch nimble else cd nim-$nim_branch git fetch origin if ! git merge FETCH_HEAD | grep "Already up-to-date"; then bin/nim c koch ./koch boot -d:release ./koch nimble fi fi cd .. before_script: - export PATH="nim-$nim_branch/bin${PATH:+:$PATH}" - export NIM_LIB_PREFIX="$(pwd)/nim-$nim_branch" script: - nimble test cache: directories: - nim-master - nim-devel dist: trusty docopt.nim-0.6.8/LICENSE000066400000000000000000000022161340260360200146010ustar00rootroot00000000000000The MIT License (MIT) Copyright (C) 2012-2014 Vladimir Keleshev Copyright (C) 2015 Oleh Prypin 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. docopt.nim-0.6.8/README.md000066400000000000000000000156761340260360200150710ustar00rootroot00000000000000[docopt][docopt.org] creates *beautiful* command-line interfaces ================================================================ **This is a port of [docopt][docopt.py] to [Nim][]. Visit [docopt.org][] for more information.** [![Build Status](https://api.travis-ci.org/docopt/docopt.nim.svg?branch=master)](https://travis-ci.org/docopt/docopt.nim) ```nim let doc = """ Naval Fate. Usage: naval_fate ship new ... naval_fate ship move [--speed=] naval_fate ship shoot naval_fate mine (set|remove) [--moored | --drifting] naval_fate (-h | --help) naval_fate --version Options: -h --help Show this screen. --version Show version. --speed= Speed in knots [default: 10]. --moored Moored (anchored) mine. --drifting Drifting mine. """ import strutils import docopt let args = docopt(doc, version = "Naval Fate 2.0") if args["move"]: echo "Moving ship $# to ($#, $#) at $# kn".format( args[""], args[""], args[""], args["--speed"]) ships[$args[""]].move( parseFloat($args[""]), parseFloat($args[""]), speed = parseFloat($args["--speed"])) if args["new"]: for name in @(args[""]): echo "Creating ship $#" % name ``` The option parser is generated based on the docstring above that is passed to `docopt` function. `docopt` parses the usage pattern (`"Usage: ..."`) and option descriptions (lines starting with dash "`-`") and ensures that the program invocation matches the usage pattern; it parses options, arguments and commands based on that. The basic idea is that *a good help message has all necessary information in it to make a parser*. Documentation ------------- ```nim proc docopt(doc: string, argv: seq[string] = nil, help = true, version: string = nil, optionsFirst = false, quit = true): Table[string, Value] ``` `docopt` takes 1 required and 5 optional arguments: - `doc` is a string that contains a **help message** that will be parsed to create the option parser. The simple rules of how to write such a help message are described at [docopt.org][]. Here is a quick example of such a string: Usage: my_program [-hso FILE] [--quiet | --verbose] [INPUT ...] -h --help show this -s --sorted sorted output -o FILE specify output file [default: ./test.txt] --quiet print less text --verbose print more text - `argv` is an optional argument vector; by default `docopt` uses the argument vector passed to your program (`commandLineParams()`). Alternatively you can supply a list of strings like `@["--verbose", "-o", "hai.txt"]`. - `help`, by default `true`, specifies whether the parser should automatically print the help message (supplied as `doc`) and terminate, in case `-h` or `--help` option is encountered (options should exist in usage pattern). If you want to handle `-h` or `--help` options manually (as other options), set `help = false`. - `version`, by default `nil`, is an optional argument that specifies the version of your program. If supplied, then, (assuming `--version` option is mentioned in usage pattern) when parser encounters the `--version` option, it will print the supplied version and terminate. `version` can be any string, e.g. `"2.1.0rc1"`. > Note, when `docopt` is set to automatically handle `-h`, `--help` and `--version` options, you still need to mention them in usage pattern for this to work. Also, for your users to know about them. - `optionsFirst`, by default `false`. If set to `true` will disallow mixing options and positional arguments. I.e. after first positional argument, all arguments will be interpreted as positional even if the look like options. This can be used for strict compatibility with POSIX, or if you want to dispatch your arguments to other programs. - `quit`, by default `true`, specifies whether [`quit()`][quit] should be called after encountering invalid arguments or printing the help message (see `help`). Setting this to `false` will allow `docopt` to raise a `DocoptExit` exception (with the `usage` member set) instead. If the `doc` string is invalid, `DocoptLanguageError` will be raised. The **return** value is a [`Table`][table] with options, arguments and commands as keys, spelled exactly like in your help message. Long versions of options are given priority. For example, if you invoke the top example as: naval_fate ship Guardian move 100 150 --speed=15 the result will be: ```nim {"--drifting": false, "mine": false, "--help": false, "move": true, "--moored": false, "new": false, "--speed": "15", "remove": false, "--version": false, "set": false, "": @["Guardian"], "ship": true, "": "100", "shoot": false, "": "150"} ``` Note that this is not how the values are actually stored, because a `Table` can hold values of only one type. For that reason, a variant `Value` type is needed. `Value`'s only accessible member is `kind: ValueKind` (which shouldn't be needed anyway, because it is known beforehand). `ValueKind` is one of: - `vkNone` (No value) This kind of `Value` appears when there is an option which hasn't been set and has no default. It is `false` when converted `toBool`. - `vkBool` (A boolean) This represents whether a boolean flag has been set or not. Just use it in a boolean context (conversion `toBool` is present). - `vkInt` (An integer) An integer represents how many times a flag has been repeated (if it is possible to supply it multiple times). Use `value.len` to obtain this `int`, or just use the value in a boolean context to find out whether this flag is present at least once. - `vkStr` (A string) Any option that has a user-supplied value will be represented as a `string` (conversion to integers, etc, does not happen). To obtain this string, use `$value`. - `vkList` (A list of strings) Any value that can be supplied multiple times will be represented by a `seq[string]`, even if the user provides just one. To obtain this `seq`, use `@value`. To obtain its length, use `value.len` or `@value.len`. To obtain the n-th value (0-indexed), both `value[i]` and `@value[i]` will work. If you are sure there is exactly one value, `$value` is the same as `value[0]`. Note that you can use any kind of value in a boolean context and convert any value to `string`. Look [in the source code](src/private/value.nim) to find out more about these conversions. Examples -------- See [examples](examples) folder. For more examples of docopt language see [docopt.py examples][]. Installation ------------ nimble install docopt This library has no dependencies outside the standard library. An impure [`re`][re] library is used. [docopt.org]: http://docopt.org/ [docopt.py]: https://github.com/docopt/docopt [docopt.py examples]: https://github.com/docopt/docopt/tree/master/examples [nim]: http://nim-lang.org/ [re]: http://nim-lang.org/re.html [table]: http://nim-lang.org/tables.html#Table [quit]: http://nim-lang.org/system.html#quit docopt.nim-0.6.8/docopt.nimble000066400000000000000000000005641340260360200162600ustar00rootroot00000000000000version = "0.6.8" author = "Oleh Prypin" description = "Command line option parser that will make you smile" license = "MIT" srcDir = "src" requires "nim >= 0.15.0" requires "regex >= 0.7.4" task test, "Test": exec "nimble c --verbosity:0 -r -y test/test" for f in listFiles("examples"): if f[^4..^1] == ".nim": exec "nim compile --verbosity:0 --hints:off " & f docopt.nim-0.6.8/examples/000077500000000000000000000000001340260360200154115ustar00rootroot00000000000000docopt.nim-0.6.8/examples/counted.nim000066400000000000000000000007521340260360200175630ustar00rootroot00000000000000let doc = """ Usage: counted --help counted -v... counted go [go] counted (--path=)... counted Try: counted -vvvvvvvvvv counted go go counted --path ./here --path ./there counted this.txt that.txt """ import strutils, unicode import docopt let args = docopt(doc) echo args if args["-v"]: echo unicode.capitalize(repeat("very ", args["-v"].len - 1) & "verbose") for path in @(args["--path"]): echo read_file(path) docopt.nim-0.6.8/examples/cycle.nim000066400000000000000000000013721340260360200172200ustar00rootroot00000000000000let doc = """ Repeatedly output the arguments. Usage: cycle [options] ... Options: -h --help show this help message and exit -n output all the arguments N times [default: infinite] --interval wait after every line of output """ import strutils, os, math import docopt let args = docopt(doc) echo args let infinite = ($args["-n"] == "infinite") var n: int if not infinite: n = parse_int($args["-n"]) var interval = 0 if args["--interval"]: interval = to_int(parse_float($args["--interval"])*1000) while true: for s in @(args[""]): echo s if interval > 0: sleep interval if not infinite: dec n if n <= 0: break docopt.nim-0.6.8/examples/nim.cfg000066400000000000000000000000351340260360200166530ustar00rootroot00000000000000path: "$project_path/../src" docopt.nim-0.6.8/examples/odd_even.nim000066400000000000000000000003721340260360200177030ustar00rootroot00000000000000let doc = """ Usage: odd_even [-h | --help] (ODD EVEN)... Example, try: odd_even 1 2 3 4 Options: -h, --help """ import docopt let args = docopt(doc) echo args for i in 0 ..< args["ODD"].len: echo args["ODD"][i] & " " & args["EVEN"][i] docopt.nim-0.6.8/src/000077500000000000000000000000001340260360200143625ustar00rootroot00000000000000docopt.nim-0.6.8/src/docopt.nim000066400000000000000000000564601340260360200163720ustar00rootroot00000000000000# Copyright (C) 2012-2014 Vladimir Keleshev # Copyright (C) 2015 Oleh Prypin # Licensed under terms of MIT license (see LICENSE) import regex, options, os, tables from sequtils import deduplicate, delete, filter_it import docopt/util export tables include docopt/value type DocoptLanguageError* = object of Exception ## Error in construction of usage-message by developer. DocoptExit* = object of Exception ## Exit in case user invoked program with incorrect arguments. usage*: string gen_class: type Pattern = ref object of RootObj m_name: string value: Value has_children: bool children: seq[Pattern] ChildPattern = ref object of Pattern ParentPattern = ref object of Pattern Argument = ref object of ChildPattern Command = ref object of Argument Option = ref object of ChildPattern short: string long: string argcount: int Required = ref object of ParentPattern Optional = ref object of ParentPattern AnyOptions = ref object of Optional ## Marker/placeholder for [options] shortcut. OneOrMore = ref object of ParentPattern Either = ref object of ParentPattern proc argument(name: string, value = val()): Argument = Argument(m_name: name, value: value) proc command(name: string, value = val(false)): Command = Command(m_name: name, value: value) proc option(short, long: string = "", argcount = 0, value = val(false)): Option = assert argcount in [0, 1] result = Option(short: short, long: long, argcount: argcount, value: value) if value.kind == vkBool and not value and argcount > 0: result.value = val() proc required(children: varargs[Pattern]): Required = Required(has_children: true, children: @children, value: val()) proc optional(children: varargs[Pattern]): Optional = Optional(has_children: true, children: @children, value: val()) proc any_options(children: varargs[Pattern]): AnyOptions = AnyOptions(has_children: true, children: @children, value: val()) proc one_or_more(children: varargs[Pattern]): OneOrMore = OneOrMore(has_children: true, children: @children, value: val()) proc either(children: varargs[Pattern]): Either = Either(has_children: true, children: @children, value: val()) type MatchResult = tuple[matched: bool; left, collected: seq[Pattern]] SingleMatchResult = tuple[pos: int, match: Pattern] {.warning[LockLevel]: off.} method str(self: Pattern): string {.base, gcsafe, nosideeffect.} = assert false method name(self: Pattern): string {.base, gcsafe.} = self.m_name method `name=`(self: Pattern, name: string) {.base, gcsafe.} = self.m_name = name proc `==`(self, other: Pattern): bool = if self.is_nil and other.is_nil: true elif not self.is_nil and not other.is_nil: self.str == other.str else: # Exactly one of the two is nil false method flat(self: Pattern, types: varargs[string]): seq[Pattern] {.base, gcsafe.} = assert false method match(self: Pattern, left: seq[Pattern], collected: seq[Pattern] = @[]): MatchResult {.base, gcsafe.} = assert false method fix_identities(self: Pattern, uniq: seq[Pattern]) {.base, gcsafe.} = ## Make pattern-tree tips point to same object if they are equal. for i, child in self.children: if not child.has_children: assert child in uniq self.children[i] = uniq[uniq.find(child)] else: child.fix_identities(uniq) method fix_identities(self: Pattern) {.base, gcsafe.} = self.fix_identities(self.flat().deduplicate()) method either(self: Pattern): Either {.base, gcsafe.} = ## Transform pattern into an equivalent, with only top-level Either. # Currently the pattern will not be equivalent, but more "narrow", # although good enough to reason about list arguments. var ret: seq[seq[Pattern]] = @[] var groups = @[@[self]] while groups.len > 0: var children = groups[0] groups.delete(0) let classes = children.map_it(string, it.class) const parents = "Required Optional AnyOptions Either OneOrMore".split() if parents.any_it(it in classes): var child: Pattern for i, c in children: if c.class in parents: child = c children.delete(i, i) break assert child != nil if child.class == "Either": for c in child.children: groups.add(@[c] & children) elif child.class == "OneOrMore": groups.add(child.children & child.children & children) else: groups.add(child.children & children) else: ret.add children either(ret.map_it(Pattern, required(it))) method fix_repeating_arguments(self: Pattern) {.base, gcsafe.} = ## Fix elements that should accumulate/increment values. var either: seq[seq[Pattern]] = @[] for child in self.either.children: either.add(@(child.children)) for cas in either: for e in cas: if cas.count(e) <= 1: continue if e.class == "Argument" or e.class == "Option" and Option(e).argcount > 0: if e.value.kind == vkNone: e.value = val(@[]) elif e.value.kind != vkList: e.value = val(($e.value).split_whitespace()) if e.class == "Command" or e.class == "Option" and Option(e).argcount == 0: e.value = val(0) method fix(self: Pattern) {.base, gcsafe.} = self.fix_identities() self.fix_repeating_arguments() method str(self: ChildPattern): string = "$#($#, $#)".format(self.class, self.name.str, self.value.str) method flat(self: ChildPattern, types: varargs[string]): seq[Pattern] = if types.len == 0 or self.class in types: @[Pattern(self)] else: @[] method single_match(self: ChildPattern, left: seq[Pattern]): SingleMatchResult {.base, gcsafe.} = assert false method match(self: ChildPattern, left: seq[Pattern], collected: seq[Pattern] = @[]): MatchResult = var m: SingleMatchResult try: m = self.single_match(left) except ValueError: return (false, left, collected) var (pos, match) = m let left2 = left[0.. 0: var m: RegexMatch if description.find(re"(?i)\[default:\ (.*)\]", m): let bounds = m.group(0)[0] value = val(description.substr(bounds.a, bounds.b)) else: value = val() constructor(short, long, argcount, value) method single_match(self: Option, left: seq[Pattern]): SingleMatchResult = for n, pattern in left: if self.name == pattern.name: return (n, pattern) raise new_exception(ValueError, "Not found") method name(self: Option): string = if self.long != "": self.long else: self.short method str(self: Option): string = "Option($#, $#, $#, $#)".format(self.short.str, self.long.str, self.argcount, self.value.str) method match(self: Required, left: seq[Pattern], collected: seq[Pattern] = @[]): MatchResult = result = (true, left, collected) for pattern in self.children: result = pattern.match(result.left, result.collected) if not result.matched: return (false, left, collected) method match(self: Optional, left: seq[Pattern], collected: seq[Pattern] = @[]): MatchResult = result = (true, left, collected) for pattern in self.children: result = pattern.match(result.left, result.collected) result.matched = true method match(self: OneOrMore, left: seq[Pattern], collected: seq[Pattern] = @[]): MatchResult = assert self.children.len == 1 result = (true, left, collected) var l2: seq[Pattern] var times = 0 while result.matched: # could it be that something didn't match but changed l or c? result = self.children[0].match(result.left, result.collected) if result.matched: times += 1 if l2 == result.left: break l2 = result.left if times >= 1: result.matched = true else: return (false, left, collected) method match(self: Either, left: seq[Pattern], collected: seq[Pattern] = @[]): MatchResult = var found = false for pattern in self.children: let outcome = pattern.match(left, collected) if outcome.matched: if not found or outcome.left.len < result.left.len: result = outcome found = true if not found: return (false, left, collected) type TokenStream = ref object tokens: seq[string] error: ref Exception proc `@`(tokens: TokenStream): var seq[string] = tokens.tokens proc token_stream(source: seq[string], error: ref Exception): TokenStream = TokenStream(tokens: source, error: error) proc token_stream(source: string, error: ref Exception): TokenStream = token_stream(source.split_whitespace(), error) proc current(self: TokenStream): string = if @self.len > 0: @self[0] else: "" proc move(self: TokenStream): string = result = self.current @self.delete(0) proc parse_long(tokens: TokenStream, options: var seq[Option]): seq[Pattern] = ## long ::= '--' chars [ ( ' ' | '=' ) chars ] ; let (long, eq, v) = tokens.move().partition("=") assert long.starts_with "--" var value = (if eq == "" and v == "": val() else: val(v)) var similar = options.filter_it(it.long == long) var o: Option if tokens.error of DocoptExit and similar.len == 0: # if no exact match similar = options.filter_it(it.long != "" and it.long.starts_with long) if similar.len > 1: # might be simply specified ambiguously 2+ times? tokens.error.msg = "$# is not a unique prefix: $#?".format( long, similar.map_it(string, it.long).join(", ")) raise tokens.error elif similar.len < 1: let argcount = (if eq == "=": 1 else: 0) o = option("", long, argcount) options.add o if tokens.error of DocoptExit: o = option("", long, argcount, if argcount > 0: value else: val(true)) else: o = option(similar[0].short, similar[0].long, similar[0].argcount, similar[0].value) if o.argcount == 0: if value.kind != vkNone: tokens.error.msg = "$# must not have an argument".format(o.long) raise tokens.error else: if value.kind == vkNone: if tokens.current == "": tokens.error.msg = "$# requires argument".format(o.long) raise tokens.error value = val(tokens.move()) if tokens.error of DocoptExit: o.value = (if value.kind != vkNone: value else: val(true)) @[Pattern(o)] proc parse_shorts(tokens: TokenStream, options: var seq[Option]): seq[Pattern] = ## shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ; let token = tokens.move() assert token.starts_with("-") and not token.starts_with("--") var left = token.substr(1) result = @[] while left != "": let short = "-" & left[0] left = left.substr(1) let similar = options.filter_it(it.short == short) var o: Option if similar.len > 1: tokens.error.msg = "$# is specified ambiguously $# times".format( short, similar.len) raise tokens.error elif similar.len < 1: o = option(short, "", 0) options.add o if tokens.error of DocoptExit: o = option(short, "", 0, val(true)) else: # why copying is necessary here? o = option(short, similar[0].long, similar[0].argcount, similar[0].value) var value = val() if o.argcount != 0: if left == "": if tokens.current == "": tokens.error.msg = "$# requires argument".format(short) raise tokens.error value = val(tokens.move()) else: value = val(left) left = "" if tokens.error of DocoptExit: o.value = (if value.kind != vkNone: value else: val(true)) result.add o proc parse_expr(tokens: TokenStream, options: var seq[Option]): seq[Pattern] {.gcsafe.} proc parse_pattern(source: string, options: var seq[Option]): Required = var tokens = token_stream( source.replace(re"([\[\]\(\)\|]|\.\.\.)", r" $1 "), new_exception(DocoptLanguageError, "") ) let ret = parse_expr(tokens, options) if tokens.current != "": tokens.error.msg = "unexpected ending: '$#'".format(@tokens.join(" ")) raise tokens.error required(ret) proc parse_seq(tokens: TokenStream, options: var seq[Option]): seq[Pattern] {.gcsafe.} proc parse_expr(tokens: TokenStream, options: var seq[Option]): seq[Pattern] = ## expr ::= seq ( '|' seq )* ; var sequ = parse_seq(tokens, options) if tokens.current != "|": return sequ var res = (if sequ.len > 1: @[Pattern(required(sequ))] else: sequ) while tokens.current == "|": discard tokens.move() sequ = parse_seq(tokens, options) res.add(if sequ.len > 1: @[Pattern(required(sequ))] else: sequ) return (if res.len > 1: @[Pattern(either(res))] else: res) proc parse_atom(tokens: TokenStream, options: var seq[Option]): seq[Pattern] {.gcsafe.} proc parse_seq(tokens: TokenStream, options: var seq[Option]): seq[Pattern] = ## seq ::= ( atom [ '...' ] )* ; result = @[] while tokens.current notin ["", "]", ")", "|"]: var atom = parse_atom(tokens, options) if tokens.current == "...": let oom = one_or_more(atom) atom = @[Pattern(oom)] discard tokens.move() result.add atom proc parse_atom(tokens: TokenStream, options: var seq[Option]): seq[Pattern] = ## atom ::= '(' expr ')' | '[' expr ']' | 'options' ## | long | shorts | argument | command ; var token = tokens.current if token in ["(", "["]: discard tokens.move() var matching: string var ret: Pattern case token of "(": matching = ")" ret = required(parse_expr(tokens, options)) of "[": matching = "]" ret = optional(parse_expr(tokens, options)) else: assert false if tokens.move() != matching: tokens.error.msg = "unmatched '$#'".format(token) raise tokens.error @[ret] elif token == "options": discard tokens.move() @[Pattern(any_options())] elif (token.starts_with "--") and token != "--": parse_long(tokens, options) elif (token.starts_with "-") and token notin ["-", "--"]: parse_shorts(tokens, options) elif (token.starts_with "<") and (token.ends_with ">") or util.is_upper(token): @[Pattern(argument(tokens.move()))] else: @[Pattern(command(tokens.move()))] proc parse_argv(tokens: TokenStream, options: var seq[Option], options_first = false): seq[Pattern] = ## Parse command-line argument vector. ## ## If options_first: ## argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; ## else: ## argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; result = @[] while tokens.current != "": if tokens.current == "--": return result & @tokens.map_it(Pattern, argument("", val(it))) elif tokens.current.starts_with "--": result.add parse_long(tokens, options) elif (tokens.current.starts_with "-") and tokens.current != "-": result.add parse_shorts(tokens, options) elif options_first: return result & @tokens.map_it(Pattern, argument("", val(it))) else: result.add argument("", val(tokens.move())) proc parse_defaults(doc: string): seq[Option] = var split = doc.split_incl(re"\n\ *(<\S+?>|-\S+?)") result = @[] for i in 1 .. split.len div 2: var s = split[i*2-1] & split[i*2] if s.starts_with "-": result.add option.option_parse(s) proc printable_usage(doc: string): string = var usage_split = doc.split_incl(re"(?i)(Usage:)") if usage_split.len < 3: raise new_exception(DocoptLanguageError, """"usage:" (case-insensitive) not found.""") if usage_split.len > 3: raise new_exception(DocoptLanguageError, """More than one "usage:" (case-insensitive).""") usage_split.delete(0) usage_split.join().split_incl(re"\n\s*\n")[0].strip() proc formal_usage(printable_usage: string): string = var pu = printable_usage.split_whitespace() pu.delete(0) var pu0 = pu[0] pu.delete(0) "( " & pu.map_it(string, if it == pu0: ") | (" else: it).join(" ") & " )" proc extras(help: bool, version: string, options: seq[Pattern], doc: string) = if help and options.any_it((it.name in ["-h", "--help"]) and it.value): echo(doc.strip()) quit() elif version != "" and options.any_it(it.name == "--version" and it.value): echo(version) quit() proc docopt_exc(doc: string, argv: seq[string], help: bool, version: string, options_first = false): Table[string, Value] = var doc = doc.replace("\r\l", "\l") var docopt_exit = new_exception(DocoptExit, "") docopt_exit.usage = printable_usage(doc) var options = parse_defaults(doc) var pattern = parse_pattern(formal_usage(docopt_exit.usage), options) var argvt = parse_argv(token_stream(argv, docopt_exit), options, options_first) var pattern_options = pattern.flat("Option").deduplicate() for any_options in pattern.flat("AnyOptions"): var doc_options = parse_defaults(doc).deduplicate() any_options.children = doc_options.filter_it( it notin pattern_options).map_it(Pattern, Pattern(it)) extras(help, version, argvt, doc) pattern.fix() var (matched, left, collected) = pattern.match(argvt) if matched and left.len == 0: # better error message if left? result = init_table[string, Value]() for a in pattern.flat(): result[a.name] = a.value for a in collected: result[a.name] = a.value else: raise docopt_exit proc docopt*(doc: string, argv: seq[string] = command_line_params(), help = true, version: string = "", options_first = false, quit = true ): Table[string, Value] {.gcsafe.} = ## Parse `argv` based on command-line interface described in `doc`. ## ## `docopt` creates your command-line interface based on its ## description that you pass as `doc`. Such description can contain ## --options, , commands, which could be ## [optional], (required), (mutually | exclusive) or repeated... ## ## Parameters ## ---------- ## doc : str ## Description of your command-line interface. ## argv : seq[string], optional ## Argument vector to be parsed. sys.argv[1:] is used if not ## provided. ## help : bool (default: true) ## Set to false to disable automatic help on -h or --help ## options. ## version : string ## If passed, the string will be printed if --version is in ## `argv`. ## options_first : bool (default: false) ## Set to true to require options precede positional arguments, ## i.e. to forbid options and positional arguments intermix. ## quit : bool (default: true) ## Set to false to let this function raise DocoptExit instead ## of printing usage and quitting the application. ## ## Returns ## ------- ## args : Table[string, Value] ## A dictionary, where keys are names of command-line elements ## such as e.g. "--verbose" and "", and values are the ## parsed values of those elements. ## ## Example ## ------- ## import tables, docopt ## ## let doc = """ ## Usage: ## my_program tcp [--timeout=] ## my_program serial [--baud=] [--timeout=] ## my_program (-h | --help | --version) ## ## Options: ## -h, --help Show this screen and exit. ## --baud= Baudrate [default: 9600] ## """ ## let argv = @["tcp", "127.0.0.1", "80", "--timeout", "30"] ## echo docopt(doc, argv) ## ## # {serial: false, : "127.0.0.1", --help: false, --timeout: "30", ## # --baud: "9600", --version: false, tcp: true, : "80"} ## ## See also ## -------- ## Full documentation: http://docopt.org/ if not quit: return docopt_exc(doc, argv, help, version, options_first) try: return docopt_exc(doc, argv, help, version, options_first) except DocoptExit: stderr.write_line((ref DocoptExit)(get_current_exception()).usage) quit() docopt.nim-0.6.8/src/docopt/000077500000000000000000000000001340260360200156525ustar00rootroot00000000000000docopt.nim-0.6.8/src/docopt/util.nim000066400000000000000000000033511340260360200173360ustar00rootroot00000000000000# Copyright (C) 2015 Oleh Prypin # Licensed under terms of MIT license (see LICENSE) import strutils, unicode, macros template any_it*(lst: typed, pred: untyped): bool = ## Does `pred` return true for any of the `it`s of `lst`? var result {.gensym.} = false for it {.inject.} in lst: if pred: result = true break result template map_it*(lst, typ: typed, op: untyped): untyped = ## Returns `seq[typ]` that contains `op` applied to each `it` of `lst` var result {.gensym.}: seq[typ] = @[] for it {.inject.} in items(lst): result.add(op) result proc count*[T](s: openarray[T], it: T): int = ## How many times this item appears in an array result = 0 for x in s: if x == it: result += 1 proc partition*(s, sep: string): tuple[left, sep, right: string] = ## "a+b".partition("+") == ("a", "+", "b") ## "a+b".partition("-") == ("a+b", "", "") assert sep != "" let pos = s.find(sep) if pos < 0: (s, "", "") else: (s.substr(0, pos.pred), s.substr(pos, pos.pred+sep.len), s.substr(pos+sep.len)) proc is_upper*(s: string): bool = ## Is the string in uppercase (and there is at least one cased character)? let upper = unicode.to_upper(s) s == upper and upper != unicode.to_lower(s) macro gen_class*(body: untyped): untyped = ## When applied to a type block, this will generate methods ## that return each type's name as a string. for typ in body[0].children: var meth = "method class(self: $1): string" if $typ[2][0][1][0] == "RootObj": meth &= "{.base, gcsafe.}" meth &= "= \"$1\"" body.add(parse_stmt(meth.format(typ[0]))) body docopt.nim-0.6.8/src/docopt/value.nim000066400000000000000000000060651340260360200175020ustar00rootroot00000000000000# Copyright (C) 2015 Oleh Prypin # Licensed under terms of MIT license (see LICENSE) import strutils import util type ValueKind* = enum vkNone, ## No value vkBool, ## A boolean vkInt, ## An integer vkStr, ## A string vkList ## A list of strings Value* = object ## docopt variant type case kind*: ValueKind of vkNone: nil of vkBool: bool_v: bool of vkInt: int_v: int of vkStr: str_v: string of vkList: list_v: seq[string] converter to_bool*(v: Value): bool = ## Convert a Value to bool, depending on its kind: ## - vkNone: false ## - vkBool: boolean value itself ## - vkInt: true if integer is not zero ## - vkStr: true if string is not empty ## - vkList: true if sequence is not empty case v.kind of vkNone: false of vkBool: v.bool_v of vkInt: v.int_v != 0 of vkStr: v.str_v.len > 0 of vkList: v.list_v.len > 0 proc len*(v: Value): int = ## Return the integer of a vkInt Value ## or the length of the seq of a vkList value. ## It is an error to use it on other kinds of Values. if v.kind == vkInt: v.int_v else: v.list_v.len proc `@`*(v: Value): seq[string] = ## Return the seq of a vkList Value. ## It is an error to use it on other kinds of Values. v.list_v proc `[]`*(v: Value, i: int): string = ## Return the i-th item of the seq of a vkList Value. ## It is an error to use it on other kinds of Values. v.list_v[i] iterator items*(v: Value): string = ## Iterate over the seq of a vkList Value. ## It is an error to use it on other kinds of Values. for val in v.list_v: yield val iterator pairs*(v: Value): tuple[key: int, val: string] = ## Iterate over the seq of a vkList Value, yielding ``(index, v[index])`` ## pairs. ## It is an error to use it on other kinds of Values. for key, val in v.list_v: yield (key: key, val: val) proc str(s: string): string = "\"" & s.replace("\"", "\\\"") & "\"" proc str[T](s: seq[T]): string = "[" & s.map_it(string, it.str).join(", ") & "]" proc str(v: Value): string = case v.kind of vkNone: "nil" of vkStr: v.str_v.str of vkInt: $v.int_v of vkBool: $v.bool_v of vkList: v.list_v.str proc `$`*(v: Value): string = ## Return the string of a vkStr Value, ## or the item of a vkList Value, if there is exactly one, ## or a string representation of any other kind of Value. if v.kind == vkStr: v.str_v elif v.kind == vkList and v.list_v.len == 1: v.list_v[0] else: v.str proc `==`*(a, b: Value): bool {.gcsafe.} = a.kind == b.kind and a.str == b.str proc val(): Value = Value(kind: vkNone) proc val(v: bool): Value = Value(kind: vkBool, bool_v: v) proc val(v: int): Value = Value(kind: vkInt, int_v: v) proc val(v: string): Value = Value(kind: vkStr, str_v: v) proc val(v: seq[string]): Value = Value(kind: vkList, list_v: v) docopt.nim-0.6.8/test/000077500000000000000000000000001340260360200145525ustar00rootroot00000000000000docopt.nim-0.6.8/test/nim.cfg000066400000000000000000000001541340260360200160160ustar00rootroot00000000000000path = "$project_path/../src" hints = off linedir = on debuginfo stacktrace = on linetrace = on threads = ondocopt.nim-0.6.8/test/test.nim000066400000000000000000000045071340260360200162440ustar00rootroot00000000000000import json, strutils, tables, options include docopt proc test(doc, args, expected_s: string): bool = var expected_json = parse_json(expected_s) var error = "" try: try: var output = docopt(doc, args.split_whitespace(), quit=false) var expected = init_table[string, Value]() for k, v in expected_json: expected[k] = case v.kind of JNull: val() of JString: val(v.str) of JInt: val(int(v.num)) of JBool: val(v.bval) of JArray: val(v.elems.map_it(string, it.str)) else: val() error = "!= " & $output assert expected == output except DocoptExit: error = "DocoptExit on valid input" assert expected_json.kind == JString and expected_json.str == "user-error" return true except AssertionError: echo "-------- TEST NOT PASSED --------" echo doc echo "$ prog ", args, " " echo expected_s echo error echo "---------------------------------" return false var args, expected: options.Option[string] var doc: string var in_doc = false var total, passed = 0 const tests = static_read("testcases.docopt") for each_line in (tests & "\n\n").split_lines(): var line = each_line.partition("#").left if not in_doc and line.starts_with("r\"\"\""): in_doc = true doc = "" line = line.substr(4) if in_doc: doc &= line if line.ends_with("\"\"\""): doc = doc[0 .. doc.len-4] in_doc = false doc &= "\n" elif line.starts_with("$ prog"): assert args.is_none and expected.is_none args = some(line.substr(7)) elif line.starts_with("{") or line.starts_with("\""): assert args.is_some and expected.is_none expected = some(line) elif line.len > 0: assert expected.is_some expected = some(expected.get & "\n" & line) if line.len == 0 and args.is_some and expected.is_some: total += 1 if test(doc, args.get, expected.get): passed += 1 stdout.write("\rTests passed: $#/$#\r".format(passed, total)) args = none(string) expected = none(string) echo() quit(if passed == total: 0 else: 1) docopt.nim-0.6.8/test/testcases.docopt000066400000000000000000000240521340260360200177650ustar00rootroot00000000000000r"""Usage: prog """ $ prog {} $ prog --xxx "user-error" r"""Usage: prog [options] -a All. """ $ prog {"-a": false} $ prog -a {"-a": true} $ prog -x "user-error" r"""Usage: prog [options] --all All. """ $ prog {"--all": false} $ prog --all {"--all": true} $ prog --xxx "user-error" r"""Usage: prog [options] -v, --verbose Verbose. """ $ prog --verbose {"--verbose": true} $ prog --ver {"--verbose": true} $ prog -v {"--verbose": true} r"""Usage: prog [options] -p PATH """ $ prog -p home/ {"-p": "home/"} $ prog -phome/ {"-p": "home/"} $ prog -p "user-error" r"""Usage: prog [options] --path """ $ prog --path home/ {"--path": "home/"} $ prog --path=home/ {"--path": "home/"} $ prog --pa home/ {"--path": "home/"} $ prog --pa=home/ {"--path": "home/"} $ prog --path "user-error" r"""Usage: prog [options] -p PATH, --path= Path to files. """ $ prog -proot {"--path": "root"} r"""Usage: prog [options] -p --path PATH Path to files. """ $ prog -p root {"--path": "root"} $ prog --path root {"--path": "root"} r"""Usage: prog [options] -p PATH Path to files [default: ./] """ $ prog {"-p": "./"} $ prog -phome {"-p": "home"} r"""UsAgE: prog [options] --path= Path to files [dEfAuLt: /root] """ $ prog {"--path": "/root"} $ prog --path=home {"--path": "home"} r"""usage: prog [options] -a Add -r Remote -m Message """ $ prog -a -r -m Hello {"-a": true, "-r": true, "-m": "Hello"} $ prog -armyourass {"-a": true, "-r": true, "-m": "yourass"} $ prog -a -r {"-a": true, "-r": true, "-m": null} r"""Usage: prog [options] --version --verbose """ $ prog --version {"--version": true, "--verbose": false} $ prog --verbose {"--version": false, "--verbose": true} $ prog --ver "user-error" $ prog --verb {"--version": false, "--verbose": true} r"""usage: prog [-a -r -m ] -a Add -r Remote -m Message """ $ prog -armyourass {"-a": true, "-r": true, "-m": "yourass"} r"""usage: prog [-armmsg] -a Add -r Remote -m Message """ $ prog -a -r -m Hello {"-a": true, "-r": true, "-m": "Hello"} r"""usage: prog -a -b -a -b """ $ prog -a -b {"-a": true, "-b": true} $ prog -b -a {"-a": true, "-b": true} $ prog -a "user-error" $ prog "user-error" r"""usage: prog (-a -b) -a -b """ $ prog -a -b {"-a": true, "-b": true} $ prog -b -a {"-a": true, "-b": true} $ prog -a "user-error" $ prog "user-error" r"""usage: prog [-a] -b -a -b """ $ prog -a -b {"-a": true, "-b": true} $ prog -b -a {"-a": true, "-b": true} $ prog -a "user-error" $ prog -b {"-a": false, "-b": true} $ prog "user-error" r"""usage: prog [(-a -b)] -a -b """ $ prog -a -b {"-a": true, "-b": true} $ prog -b -a {"-a": true, "-b": true} $ prog -a "user-error" $ prog -b "user-error" $ prog {"-a": false, "-b": false} r"""usage: prog (-a|-b) -a -b """ $ prog -a -b "user-error" $ prog "user-error" $ prog -a {"-a": true, "-b": false} $ prog -b {"-a": false, "-b": true} r"""usage: prog [ -a | -b ] -a -b """ $ prog -a -b "user-error" $ prog {"-a": false, "-b": false} $ prog -a {"-a": true, "-b": false} $ prog -b {"-a": false, "-b": true} r"""usage: prog """ $ prog 10 {"": "10"} $ prog 10 20 "user-error" $ prog "user-error" r"""usage: prog [] """ $ prog 10 {"": "10"} $ prog 10 20 "user-error" $ prog {"": null} r"""usage: prog """ $ prog 10 20 40 {"": "10", "": "20", "": "40"} $ prog 10 20 "user-error" $ prog "user-error" r"""usage: prog [ ] """ $ prog 10 20 40 {"": "10", "": "20", "": "40"} $ prog 10 20 {"": "10", "": "20", "": null} $ prog "user-error" r"""usage: prog [ | ] """ $ prog 10 20 40 "user-error" $ prog 20 40 {"": null, "": "20", "": "40"} $ prog {"": null, "": null, "": null} r"""usage: prog ( --all | ) --all """ $ prog 10 --all {"": "10", "--all": true, "": null} $ prog 10 {"": null, "--all": false, "": "10"} $ prog "user-error" r"""usage: prog [ ] """ $ prog 10 20 {"": ["10", "20"]} $ prog 10 {"": ["10"]} $ prog {"": []} r"""usage: prog [( )] """ $ prog 10 20 {"": ["10", "20"]} $ prog 10 "user-error" $ prog {"": []} r"""usage: prog NAME... """ $ prog 10 20 {"NAME": ["10", "20"]} $ prog 10 {"NAME": ["10"]} $ prog "user-error" r"""usage: prog [NAME]... """ $ prog 10 20 {"NAME": ["10", "20"]} $ prog 10 {"NAME": ["10"]} $ prog {"NAME": []} r"""usage: prog [NAME...] """ $ prog 10 20 {"NAME": ["10", "20"]} $ prog 10 {"NAME": ["10"]} $ prog {"NAME": []} r"""usage: prog [NAME [NAME ...]] """ $ prog 10 20 {"NAME": ["10", "20"]} $ prog 10 {"NAME": ["10"]} $ prog {"NAME": []} r"""usage: prog (NAME | --foo NAME) --foo """ $ prog 10 {"NAME": "10", "--foo": false} $ prog --foo 10 {"NAME": "10", "--foo": true} $ prog --foo=10 "user-error" r"""usage: prog (NAME | --foo) [--bar | NAME] --foo --bar """ $ prog 10 {"NAME": ["10"], "--foo": false, "--bar": false} $ prog 10 20 {"NAME": ["10", "20"], "--foo": false, "--bar": false} $ prog --foo --bar {"NAME": [], "--foo": true, "--bar": true} r"""Naval Fate. Usage: prog ship new ... prog ship [] move [--speed=] prog ship shoot prog mine (set|remove) [--moored|--drifting] prog -h | --help prog --version Options: -h --help Show this screen. --version Show version. --speed= Speed in knots [default: 10]. --moored Mored (anchored) mine. --drifting Drifting mine. """ $ prog ship Guardian move 150 300 --speed=20 {"--drifting": false, "--help": false, "--moored": false, "--speed": "20", "--version": false, "": ["Guardian"], "": "150", "": "300", "mine": false, "move": true, "new": false, "remove": false, "set": false, "ship": true, "shoot": false} r"""usage: prog --hello """ $ prog --hello {"--hello": true} r"""usage: prog [--hello=] """ $ prog {"--hello": null} $ prog --hello wrld {"--hello": "wrld"} r"""usage: prog [-o] """ $ prog {"-o": false} $ prog -o {"-o": true} r"""usage: prog [-opr] """ $ prog -op {"-o": true, "-p": true, "-r": false} r"""usage: prog --aabb | --aa """ $ prog --aa {"--aabb": false, "--aa": true} $ prog --a "user-error" # not a unique prefix # # Counting number of flags # r"""Usage: prog -v """ $ prog -v {"-v": true} r"""Usage: prog [-v -v] """ $ prog {"-v": 0} $ prog -v {"-v": 1} $ prog -vv {"-v": 2} r"""Usage: prog -v ... """ $ prog "user-error" $ prog -v {"-v": 1} $ prog -vv {"-v": 2} $ prog -vvvvvv {"-v": 6} r"""Usage: prog [-v | -vv | -vvv] This one is probably most readable user-friednly variant. """ $ prog {"-v": 0} $ prog -v {"-v": 1} $ prog -vv {"-v": 2} $ prog -vvvv "user-error" r"""usage: prog [--ver --ver] """ $ prog --ver --ver {"--ver": 2} # # Counting commands # r"""usage: prog [go] """ $ prog go {"go": true} r"""usage: prog [go go] """ $ prog {"go": 0} $ prog go {"go": 1} $ prog go go {"go": 2} $ prog go go go "user-error" r"""usage: prog go... """ $ prog go go go go go {"go": 5} # # Test [options] shourtcut # r"""Usage: prog [options] A -q Be quiet -v Be verbose. """ $ prog arg {"A": "arg", "-v": false, "-q": false} $ prog -v arg {"A": "arg", "-v": true, "-q": false} $ prog -q arg {"A": "arg", "-v": false, "-q": true} # # Test single dash # r"""usage: prog [-]""" $ prog - {"-": true} $ prog {"-": false} # # If argument is repeated, its value should always be a list # r"""usage: prog [NAME [NAME ...]]""" $ prog a b {"NAME": ["a", "b"]} $ prog {"NAME": []} # # Option's argument defaults to null/None # r"""usage: prog [options] -a Add -m Message """ $ prog -a {"-m": null, "-a": true} # # Test options without description # r"""usage: prog --hello""" $ prog --hello {"--hello": true} r"""usage: prog [--hello=]""" $ prog {"--hello": null} $ prog --hello wrld {"--hello": "wrld"} r"""usage: prog [-o]""" $ prog {"-o": false} $ prog -o {"-o": true} r"""usage: prog [-opr]""" $ prog -op {"-o": true, "-p": true, "-r": false} r"""usage: git [-v | --verbose]""" $ prog -v {"-v": true, "--verbose": false} r"""usage: git remote [-v | --verbose]""" $ prog remote -v {"remote": true, "-v": true, "--verbose": false} # # Test empty usage pattern # r"""usage: prog""" $ prog {} r"""usage: prog prog """ $ prog 1 2 {"": "1", "": "2"} $ prog {"": null, "": null} r"""usage: prog prog """ $ prog {"": null, "": null} # # Option's argument should not capture default value from usage pattern # r"""usage: prog [--file=]""" $ prog {"--file": null} r"""usage: prog [--file=] --file """ $ prog {"--file": null} r"""Usage: prog [-a ] -a, --address TCP address [default: localhost:6283]. """ $ prog {"--address": "localhost:6283"} # # If option with argument could be repeated, # its arguments should be accumulated into a list # r"""usage: prog --long= ...""" $ prog --long one {"--long": ["one"]} $ prog --long one --long two {"--long": ["one", "two"]} # # Test multiple elements repeated at once # r"""usage: prog (go --speed=)...""" $ prog go left --speed=5 go right --speed=9 {"go": 2, "": ["left", "right"], "--speed": ["5", "9"]} # # Required options should work with option shortcut # r"""usage: prog [options] -a -a """ $ prog -a {"-a": true} # # If option could be repeated its defaults should be split into a list # r"""usage: prog [-o ]... -o [default: x] """ $ prog -o this -o that {"-o": ["this", "that"]} $ prog {"-o": ["x"]} r"""usage: prog [-o ]... -o [default: x y] """ $ prog -o this {"-o": ["this"]} $ prog {"-o": ["x", "y"]} # # Test stacked option's argument # r"""usage: prog -pPATH -p PATH """ $ prog -pHOME {"-p": "HOME"} # # Issue 56: Repeated mutually exclusive args give nested lists sometimes # r"""Usage: foo (--xx=x|--yy=y)...""" $ prog --xx=1 --yy=2 {"--xx": ["1"], "--yy": ["2"]}