OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library args.src.parser; | |
6 | |
7 import 'arg_parser.dart'; | 5 import 'arg_parser.dart'; |
| 6 import 'arg_parser_exception.dart'; |
8 import 'arg_results.dart'; | 7 import 'arg_results.dart'; |
9 import 'option.dart'; | 8 import 'option.dart'; |
10 | 9 |
11 final _SOLO_OPT = new RegExp(r'^-([a-zA-Z0-9])$'); | 10 final _SOLO_OPT = new RegExp(r'^-([a-zA-Z0-9])$'); |
12 final _ABBR_OPT = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); | 11 final _ABBR_OPT = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); |
13 final _LONG_OPT = new RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); | 12 final _LONG_OPT = new RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); |
14 | 13 |
15 /// The actual argument parsing class. | 14 /// The actual argument parsing class. |
16 /// | 15 /// |
17 /// Unlike [ArgParser] which is really more an "arg grammar", this is the class | 16 /// Unlike [ArgParser] which is really more an "arg grammar", this is the class |
(...skipping 12 matching lines...) Expand all Loading... |
30 | 29 |
31 /// The arguments being parsed. | 30 /// The arguments being parsed. |
32 final List<String> args; | 31 final List<String> args; |
33 | 32 |
34 /// The remaining non-option, non-command arguments. | 33 /// The remaining non-option, non-command arguments. |
35 final rest = <String>[]; | 34 final rest = <String>[]; |
36 | 35 |
37 /// The accumulated parsed options. | 36 /// The accumulated parsed options. |
38 final Map<String, dynamic> results = <String, dynamic>{}; | 37 final Map<String, dynamic> results = <String, dynamic>{}; |
39 | 38 |
40 Parser(this.commandName, this.grammar, this.args, this.parent, rest) { | 39 Parser(this.commandName, this.grammar, this.args, |
| 40 [this.parent, List<String> rest]) { |
41 if (rest != null) this.rest.addAll(rest); | 41 if (rest != null) this.rest.addAll(rest); |
42 } | 42 } |
43 | 43 |
44 /// The current argument being parsed. | 44 /// The current argument being parsed. |
45 String get current => args[0]; | 45 String get current => args[0]; |
46 | 46 |
47 /// Parses the arguments. This can only be called once. | 47 /// Parses the arguments. This can only be called once. |
48 ArgResults parse() { | 48 ArgResults parse() { |
49 var arguments = args.toList(); | 49 var arguments = args.toList(); |
50 var commandResults = null; | 50 var commandResults = null; |
51 | 51 |
52 // Parse the args. | 52 // Parse the args. |
53 while (args.length > 0) { | 53 while (args.length > 0) { |
54 if (current == '--') { | 54 if (current == '--') { |
55 // Reached the argument terminator, so stop here. | 55 // Reached the argument terminator, so stop here. |
56 args.removeAt(0); | 56 args.removeAt(0); |
57 break; | 57 break; |
58 } | 58 } |
59 | 59 |
60 // Try to parse the current argument as a command. This happens before | 60 // Try to parse the current argument as a command. This happens before |
61 // options so that commands can have option-like names. | 61 // options so that commands can have option-like names. |
62 var command = grammar.commands[current]; | 62 var command = grammar.commands[current]; |
63 if (command != null) { | 63 if (command != null) { |
64 validate(rest.isEmpty, 'Cannot specify arguments before a command.'); | 64 validate(rest.isEmpty, 'Cannot specify arguments before a command.'); |
65 var commandName = args.removeAt(0); | 65 var commandName = args.removeAt(0); |
66 var commandParser = new Parser(commandName, command, args, this, rest); | 66 var commandParser = new Parser(commandName, command, args, this, rest); |
67 commandResults = commandParser.parse(); | 67 |
| 68 try { |
| 69 commandResults = commandParser.parse(); |
| 70 } on ArgParserException catch (error) { |
| 71 if (commandName == null) rethrow; |
| 72 throw new ArgParserException( |
| 73 error.message, |
| 74 [commandName]..addAll(error.commands)); |
| 75 } |
68 | 76 |
69 // All remaining arguments were passed to command so clear them here. | 77 // All remaining arguments were passed to command so clear them here. |
70 rest.clear(); | 78 rest.clear(); |
71 break; | 79 break; |
72 } | 80 } |
73 | 81 |
74 // Try to parse the current argument as an option. Note that the order | 82 // Try to parse the current argument as an option. Note that the order |
75 // here matters. | 83 // here matters. |
76 if (parseSoloOption()) continue; | 84 if (parseSoloOption()) continue; |
77 if (parseAbbreviation(this)) continue; | 85 if (parseAbbreviation(this)) continue; |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 // Walk up to the parent command if possible. | 244 // Walk up to the parent command if possible. |
237 validate(parent != null, 'Could not find an option named "$name".'); | 245 validate(parent != null, 'Could not find an option named "$name".'); |
238 return parent.parseLongOption(); | 246 return parent.parseLongOption(); |
239 } | 247 } |
240 | 248 |
241 return true; | 249 return true; |
242 } | 250 } |
243 | 251 |
244 /// Called during parsing to validate the arguments. | 252 /// Called during parsing to validate the arguments. |
245 /// | 253 /// |
246 /// Throws a [FormatException] if [condition] is `false`. | 254 /// Throws an [ArgParserException] if [condition] is `false`. |
247 void validate(bool condition, String message) { | 255 void validate(bool condition, String message) { |
248 if (!condition) throw new FormatException(message); | 256 if (!condition) throw new ArgParserException(message); |
249 } | 257 } |
250 | 258 |
251 /// Validates and stores [value] as the value for [option], which must not be | 259 /// Validates and stores [value] as the value for [option], which must not be |
252 /// a flag. | 260 /// a flag. |
253 void setOption(Map results, Option option, String value) { | 261 void setOption(Map results, Option option, String value) { |
254 assert(!option.isFlag); | 262 assert(!option.isFlag); |
255 | 263 |
256 if (!option.isMultiple) { | 264 if (!option.isMultiple) { |
257 _validateAllowed(option, value); | 265 _validateAllowed(option, value); |
258 results[option.name] = value; | 266 results[option.name] = value; |
259 return; | 267 return; |
260 } | 268 } |
261 | 269 |
262 var list = results.putIfAbsent(option.name, () => []); | 270 var list = results.putIfAbsent(option.name, () => <String>[]); |
263 | 271 |
264 if (option.splitCommas) { | 272 if (option.splitCommas) { |
265 for (var element in value.split(",")) { | 273 for (var element in value.split(",")) { |
266 _validateAllowed(option, element); | 274 _validateAllowed(option, element); |
267 list.add(element); | 275 list.add(element); |
268 } | 276 } |
269 } else { | 277 } else { |
270 _validateAllowed(option, value); | 278 _validateAllowed(option, value); |
271 list.add(value); | 279 list.add(value); |
272 } | 280 } |
273 } | 281 } |
274 | 282 |
275 /// Validates and stores [value] as the value for [option], which must be a | 283 /// Validates and stores [value] as the value for [option], which must be a |
276 /// flag. | 284 /// flag. |
277 void setFlag(Map results, Option option, bool value) { | 285 void setFlag(Map results, Option option, bool value) { |
278 assert(option.isFlag); | 286 assert(option.isFlag); |
279 results[option.name] = value; | 287 results[option.name] = value; |
280 } | 288 } |
281 | 289 |
282 /// Validates that [value] is allowed as a value of [option]. | 290 /// Validates that [value] is allowed as a value of [option]. |
283 void _validateAllowed(Option option, String value) { | 291 void _validateAllowed(Option option, String value) { |
284 if (option.allowed == null) return; | 292 if (option.allowed == null) return; |
285 | 293 |
286 validate(option.allowed.contains(value), | 294 validate(option.allowed.contains(value), |
287 '"$value" is not an allowed value for option "${option.name}".'); | 295 '"$value" is not an allowed value for option "${option.name}".'); |
288 } | 296 } |
289 } | 297 } |
OLD | NEW |