Skip to content

Commit b959f42

Browse files
committed
Apply changes according to docopt v0.6.2
https://github.com/docopt/docopt/tree/0.6.2
1 parent a2439d2 commit b959f42

File tree

2 files changed

+192
-249
lines changed

2 files changed

+192
-249
lines changed

src/docopt.nim

Lines changed: 76 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,13 @@ type Pattern = ref object of RootObj
121121
children: seq[Pattern]
122122
gen_class(Pattern)
123123

124-
type LeafPattern = ref object of Pattern
125-
## Leaf/terminal node of a pattern tree.
126-
gen_class(LeafPattern)
124+
type ChildPattern = ref object of Pattern
125+
gen_class(ChildPattern)
127126

128-
type BranchPattern = ref object of Pattern
129-
## Branch/inner node of a pattern tree.
130-
gen_class(BranchPattern)
127+
type ParentPattern = ref object of Pattern
128+
gen_class(ParentPattern)
131129

132-
type Argument = ref object of LeafPattern
130+
type Argument = ref object of ChildPattern
133131
gen_class(Argument)
134132

135133
proc argument(name: string, value = val()): Argument =
@@ -141,7 +139,7 @@ gen_class(Command)
141139
proc command(name: string, value = val(false)): Command =
142140
Command(m_name: name, value: value)
143141

144-
type Option = ref object of LeafPattern
142+
type Option = ref object of ChildPattern
145143
short: string
146144
long: string
147145
argcount: int
@@ -155,32 +153,32 @@ proc option(short, long: string = nil, argcount = 0,
155153
if value.kind == vkBool and value.bool_v == false and argcount > 0:
156154
result.value = val()
157155

158-
type Required = ref object of BranchPattern
156+
type Required = ref object of ParentPattern
159157
gen_class(Required)
160158

161159
proc required(children: openarray[Pattern]): Required =
162160
Required(children: @children)
163161

164-
type Optional = ref object of BranchPattern
162+
type Optional = ref object of ParentPattern
165163
gen_class(Optional)
166164

167165
proc optional(children: openarray[Pattern]): Optional =
168166
Optional(children: @children)
169167

170-
type OptionsShortcut = ref object of Optional
168+
type AnyOptions = ref object of Optional
171169
## Marker/placeholder for [options] shortcut.
172-
gen_class(OptionsShortcut)
170+
gen_class(AnyOptions)
173171

174-
proc options_shortcut(children: openarray[Pattern]): OptionsShortcut =
175-
OptionsShortcut(children: @children)
172+
proc any_options(children: openarray[Pattern]): AnyOptions =
173+
AnyOptions(children: @children)
176174

177-
type OneOrMore = ref object of BranchPattern
175+
type OneOrMore = ref object of ParentPattern
178176
gen_class(OneOrMore)
179177

180178
proc one_or_more(children: openarray[Pattern]): OneOrMore =
181179
OneOrMore(children: @children)
182180

183-
type Either = ref object of BranchPattern
181+
type Either = ref object of ParentPattern
184182
gen_class(Either)
185183

186184
proc either(children: seq[Pattern]): Either =
@@ -224,18 +222,17 @@ method fix_identities(self: Pattern, uniq: seq[Pattern]) =
224222
method fix_identities(self: Pattern) =
225223
self.fix_identities(self.flat([]).deduplicate())
226224

227-
method transform(pattern: Pattern): Either =
228-
## Expand pattern into an (almost) equivalent one, but with single Either.
229-
##
230-
## Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d)
231-
## Quirks: [-a] => (-a), (-a...) => (-a -a)
225+
method either(self: Pattern): Either =
226+
## Transform pattern into an equivalent, with only top-level Either.
227+
# Currently the pattern will not be equivalent, but more "narrow",
228+
# although good enough to reason about list arguments.
232229
var result: seq[seq[Pattern]] = @[]
233-
var groups: seq[seq[Pattern]] = @[@[pattern]]
230+
var groups: seq[seq[Pattern]] = @[@[self]]
234231
while groups.len > 0:
235232
var children = groups[0]
236233
groups.delete()
237234
var classes = children.map_it(string, it.class)
238-
var parents = ["Required", "Optional", "OptionsShortcut",
235+
var parents = ["Required", "Optional", "AnyOptions",
239236
"Either", "OneOrMore"]
240237
if parents.any_it(it in classes):
241238
var child: Pattern
@@ -259,7 +256,7 @@ method transform(pattern: Pattern): Either =
259256
method fix_repeating_arguments(self: Pattern) =
260257
## Fix elements that should accumulate/increment values.
261258
var either: seq[seq[Pattern]] = @[]
262-
for child in transform(self).children:
259+
for child in self.either.children:
263260
either.add(@(child.children))
264261
for cas in either:
265262
for e in cas:
@@ -280,17 +277,17 @@ method fix(self: Pattern) =
280277
self.fix_repeating_arguments()
281278

282279

283-
method str(self: LeafPattern): string =
280+
method str(self: ChildPattern): string =
284281
"$#($#, $#)".format(self.class, self.name.str, self.value.str)
285282

286-
method flat(self: LeafPattern, types: openarray[string]): seq[Pattern] =
283+
method flat(self: ChildPattern, types: openarray[string]): seq[Pattern] =
287284
if types.len == 0 or self.class in types: @[Pattern(self)] else: @[]
288285

289-
method single_match(self: LeafPattern,
286+
method single_match(self: ChildPattern,
290287
left: seq[Pattern]): SingleMatchResult =
291288
assert false; nil
292289

293-
method match(self: LeafPattern, left: seq[Pattern],
290+
method match(self: ChildPattern, left: seq[Pattern],
294291
collected: seq[Pattern] = @[]): MatchResult =
295292
var m: SingleMatchResult
296293
try:
@@ -320,10 +317,10 @@ method match(self: LeafPattern, left: seq[Pattern],
320317
return (true, left2, collected & @[match])
321318

322319

323-
method str(self: BranchPattern): string =
320+
method str(self: ParentPattern): string =
324321
"$#($#)".format(self.class, self.children.str)
325322

326-
method flat(self: BranchPattern, types: openarray[string]): seq[Pattern] =
323+
method flat(self: ParentPattern, types: openarray[string]): seq[Pattern] =
327324
if self.class in types:
328325
return @[Pattern(self)]
329326
result = new_seq[Pattern]()
@@ -448,31 +445,27 @@ method match(self: Either, left: seq[Pattern],
448445
return (false, left, collected)
449446
450447
451-
type Tokens = ref object
448+
type TokenStream = ref object
452449
tokens: seq[string]
453450
error: ref Exception
454451
455-
proc `@`(tokens: Tokens): var seq[string] = tokens.tokens
456-
457-
proc tokens(source: seq[string],
458-
error: ref Exception = new_exception(DocoptExit, "")): Tokens =
459-
Tokens(tokens: source, error: error)
452+
proc `@`(tokens: TokenStream): var seq[string] = tokens.tokens
460453
461-
proc tokens_from_pattern(source: string): Tokens =
462-
var source = source.replacef(re"([\[\]\(\)\|]|\.\.\.)", r" $1 ")
463-
var tokens = source.split_inc(re"\s+|(\S*<.*?>)").filter_it(it.len > 0)
464-
tokens(tokens, new_exception(DocoptLanguageError, ""))
454+
proc token_stream(source: seq[string], error: ref Exception): TokenStream =
455+
TokenStream(tokens: source, error: error)
456+
proc token_stream(source: string, error: ref Exception): TokenStream =
457+
token_stream(source.split(), error)
465458
466-
proc current(self: Tokens): string =
459+
proc current(self: TokenStream): string =
467460
if @self.len > 0:
468461
result = @self[0]
469462
470-
proc move(self: Tokens): string =
463+
proc move(self: TokenStream): string =
471464
result = self.current
472465
@self.delete()
473466
474467
475-
proc parse_long(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
468+
proc parse_long(tokens: TokenStream, options: var seq[Option]): seq[Pattern] =
476469
## long ::= '--' chars [ ( ' ' | '=' ) chars ] ;
477470
var (long, eq, v) = tokens.move().partition("=")
478471
assert long.starts_with("--")
@@ -502,7 +495,7 @@ proc parse_long(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
502495
raise tokens.error
503496
else:
504497
if value.kind == vkNone:
505-
if tokens.current in [nil, "--"]:
498+
if tokens.current == nil:
506499
tokens.error.msg = "$# requires argument".format(o.long)
507500
raise tokens.error
508501
value = val(tokens.move())
@@ -511,7 +504,7 @@ proc parse_long(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
511504
@[Pattern(o)]
512505
513506
514-
proc parse_shorts(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
507+
proc parse_shorts(tokens: TokenStream, options: var seq[Option]): seq[Pattern] =
515508
## shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;
516509
var token = tokens.move()
517510
assert token.starts_with("-") and not token.starts_with("--")
@@ -537,7 +530,7 @@ proc parse_shorts(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
537530
var value = val()
538531
if o.argcount != 0:
539532
if left == "":
540-
if tokens.current in [nil, "--"]:
533+
if tokens.current == nil:
541534
tokens.error.msg = "$# requires argument".format(short)
542535
raise tokens.error
543536
value = val(tokens.move())
@@ -549,20 +542,23 @@ proc parse_shorts(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
549542
result.add(o)
550543
551544
552-
proc parse_expr(tokens: Tokens, options: var seq[Option]): seq[Pattern]
545+
proc parse_expr(tokens: TokenStream, options: var seq[Option]): seq[Pattern]
553546
554547
proc parse_pattern(source: string, options: var seq[Option]): Required =
555-
var tokens = tokens_from_pattern(source)
548+
var tokens = token_stream(
549+
source.replacef(re"([\[\]\(\)\|]|\.\.\.)", r" $1 "),
550+
new_exception(DocoptLanguageError, "")
551+
)
556552
var result = parse_expr(tokens, options)
557553
if tokens.current != nil:
558554
tokens.error.msg = "unexpected ending: '$#'".format(@tokens.join(" "))
559555
raise tokens.error
560556
required(result)
561557
562558
563-
proc parse_seq(tokens: Tokens, options: var seq[Option]): seq[Pattern]
559+
proc parse_seq(tokens: TokenStream, options: var seq[Option]): seq[Pattern]
564560
565-
proc parse_expr(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
561+
proc parse_expr(tokens: TokenStream, options: var seq[Option]): seq[Pattern] =
566562
## expr ::= seq ( '|' seq )* ;
567563
var sequ = parse_seq(tokens, options)
568564
if tokens.current != "|":
@@ -576,9 +572,9 @@ proc parse_expr(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
576572
577573
578574
579-
proc parse_atom(tokens: Tokens, options: var seq[Option]): seq[Pattern]
575+
proc parse_atom(tokens: TokenStream, options: var seq[Option]): seq[Pattern]
580576
581-
proc parse_seq(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
577+
proc parse_seq(tokens: TokenStream, options: var seq[Option]): seq[Pattern] =
582578
## seq ::= ( atom [ '...' ] )* ;
583579
result = @[]
584580
while tokens.current notin [nil, "]", ")", "|"]:
@@ -590,7 +586,7 @@ proc parse_seq(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
590586
result.add(atom)
591587
592588
593-
proc parse_atom(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
589+
proc parse_atom(tokens: TokenStream, options: var seq[Option]): seq[Pattern] =
594590
## atom ::= '(' expr ')' | '[' expr ']' | 'options'
595591
## | long | shorts | argument | command ;
596592
var token = tokens.current
@@ -613,7 +609,7 @@ proc parse_atom(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
613609
return @[result]
614610
elif token == "options":
615611
discard tokens.move()
616-
return @[Pattern(options_shortcut([]))]
612+
return @[Pattern(any_options([]))]
617613
elif token.starts_with("--") and token != "--":
618614
return parse_long(tokens, options)
619615
elif token.starts_with("-") and token notin ["-", "--"]:
@@ -624,7 +620,7 @@ proc parse_atom(tokens: Tokens, options: var seq[Option]): seq[Pattern] =
624620
return @[Pattern(command(tokens.move()))]
625621
626622
627-
proc parse_argv(tokens: Tokens, options: var seq[Option],
623+
proc parse_argv(tokens: TokenStream, options: var seq[Option],
628624
options_first = false): seq[Pattern] =
629625
## Parse command-line argument vector.
630626
##
@@ -646,29 +642,30 @@ proc parse_argv(tokens: Tokens, options: var seq[Option],
646642
result.add(argument(nil, val(tokens.move())))
647643
648644
649-
proc parse_section(name: string, source: string): seq[string]
650-
651645
proc parse_defaults(doc: string): seq[Option] =
652-
result = @[]
653-
for ss in parse_section("options:", doc):
654-
# FIXME corner case "bla: options: --foo"
655-
var s = ss.partition(":").right # get rid of "options:"
656-
var split = ("\n" & s).split_inc(re"\n[\ \t]*(-\S+?)")
657-
for i in 1 .. split.len div 2:
658-
var s = split[i*2-1] & split[i*2]
659-
if s.starts_with("-"):
660-
result.add(option.option_parse(s))
646+
var split = doc.split_inc(re"\n\ *(<\S+?>|-\S+?)")
647+
result = new_seq[Option]()
648+
for i in 1 .. split.len div 2:
649+
var s = split[i*2-1] & split[i*2]
650+
if s.starts_with("-"):
651+
result.add(option.option_parse(s))
661652
662653
663-
proc parse_section(name: string, source: string): seq[string] =
664-
let pattern = re(r"^([^\n]*" & name & r"[^\n]*\n?(?:[ \t].*?(?:\n|$))*)",
665-
{reIgnoreCase, reMultiLine})
666-
@(source.find_all(pattern)).map_it(string, it.strip())
654+
proc printable_usage(doc: string): string =
655+
var usage_split = doc.split_inc(re"([Uu][Ss][Aa][Gg][Ee]:)")
656+
if usage_split.len < 3:
657+
raise new_exception(DocoptLanguageError,
658+
"\"usage:\" (case-insensitive) not found.")
659+
if usage_split.len > 3:
660+
raise new_exception(DocoptLanguageError,
661+
"More than one \"usage:\" (case-insensitive).")
662+
usage_split.delete()
663+
usage_split.join().split_inc(re"\n\s*\n")[0].strip()
667664
668665
669-
proc formal_usage(section: string): string =
670-
var section = section.partition(":").right # drop "usage:"
671-
var pu = section.split()
666+
proc formal_usage(printable_usage: string): string =
667+
var pu = printable_usage.split()
668+
pu.delete()
672669
var pu0 = pu[0]
673670
pu.delete()
674671
"( " & pu.map_it(string, if it == pu0: ") | (" else: it).join(" ") & " )"
@@ -688,25 +685,19 @@ proc docopt_exc(doc: string, argv: seq[string], help: bool, version: string,
688685
options_first = false): Table[string, Value] =
689686
690687
var argv = (if argv.is_nil: command_line_params() else: argv)
691-
692-
var usage_sections = parse_section("usage:", doc)
693-
if usage_sections.len == 0:
694-
raise new_exception(DocoptLanguageError,
695-
"\"usage:\" (case-insensitive) not found.")
696-
if usage_sections.len > 1:
697-
raise new_exception(DocoptLanguageError,
698-
"More than one \"usage:\" (case-insensitive).")
688+
699689
var docopt_exit = new_exception(DocoptExit, "")
700-
docopt_exit.usage = usage_sections[0]
690+
docopt_exit.usage = printable_usage(doc)
701691
702692
var options = parse_defaults(doc)
703693
var pattern = parse_pattern(formal_usage(docopt_exit.usage), options)
704694
705-
var argvt = parse_argv(tokens(argv), options, options_first)
695+
var argvt = parse_argv(token_stream(argv, docopt_exit), options,
696+
options_first)
706697
var pattern_options = pattern.flat(["Option"]).deduplicate()
707-
for options_shortcut in pattern.flat(["OptionsShortcut"]):
698+
for any_options in pattern.flat(["AnyOptions"]):
708699
var doc_options = parse_defaults(doc).deduplicate()
709-
options_shortcut.children = doc_options.filter_it(
700+
any_options.children = doc_options.filter_it(
710701
it notin pattern_options).map_it(Pattern, Pattern(it))
711702
712703
extras(help, version, argvt, doc)

0 commit comments

Comments
 (0)