Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Side by Side Diff: lib/args/args.dart

Issue 10855084: Added support for multi-valued options (options that can occur more than once). (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/lib/args/args_test.dart » ('j') | tests/lib/args/args_test.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** 5 /**
6 * This library lets you define parsers for parsing raw command-line arguments 6 * This library lets you define parsers for parsing raw command-line arguments
7 * into a set of options and values using [GNU][] and [POSIX][] style options. 7 * into a set of options and values using [GNU][] and [POSIX][] style options.
8 * 8 *
9 * ## Defining options ## 9 * ## Defining options ##
10 * 10 *
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
116 * you define: 116 * you define:
117 * 117 *
118 * parser.addFlag('verbose', abbr: 'v'); 118 * parser.addFlag('verbose', abbr: 'v');
119 * parser.addFlag('french', abbr: 'f'); 119 * parser.addFlag('french', abbr: 'f');
120 * parser.addFlag('iambic-pentameter', abbr: 'i'); 120 * parser.addFlag('iambic-pentameter', abbr: 'i');
121 * 121 *
122 * Then all three flags could be set using: 122 * Then all three flags could be set using:
123 * 123 *
124 * -vfi 124 * -vfi
125 * 125 *
126 * By default, an option has only a single value, with later option values
127 * overriding earlier ones, unless you set the [multiValued] flag. In that
128 * case the option can occur multiple times and when parsing arguments a
129 * List of values will be returned.
Bob Nystrom 2012/08/09 23:08:40 You should show an example of this.
gram 2012/08/09 23:47:00 Done.
130 *
126 * ## Usage ## 131 * ## Usage ##
127 * 132 *
128 * This library can also be used to automatically generate nice usage help 133 * This library can also be used to automatically generate nice usage help
129 * text like you get when you run a program with `--help`. To use this, you 134 * text like you get when you run a program with `--help`. To use this, you
130 * will also want to provide some help text when you create your options. To 135 * will also want to provide some help text when you create your options. To
131 * define help text for the entire option, do: 136 * define help text for the entire option, do:
132 * 137 *
133 * parser.addOption('mode', help: 'The compiler configuration', 138 * parser.addOption('mode', help: 'The compiler configuration',
134 * allowed: ['debug', 'release']); 139 * allowed: ['debug', 'release']);
135 * parser.addFlag('verbose', help: 'Show additional diagnostic info'); 140 * parser.addFlag('verbose', help: 'Show additional diagnostic info');
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 } 212 }
208 213
209 /** 214 /**
210 * Defines a value-taking option. Throws an [IllegalArgumentException] if: 215 * Defines a value-taking option. Throws an [IllegalArgumentException] if:
211 * 216 *
212 * * There is already an option with name [name]. 217 * * There is already an option with name [name].
213 * * There is already an option using abbreviation [abbr]. 218 * * There is already an option using abbreviation [abbr].
214 */ 219 */
215 void addOption(String name, [String abbr, String help, List<String> allowed, 220 void addOption(String name, [String abbr, String help, List<String> allowed,
216 Map<String, String> allowedHelp, String defaultsTo, 221 Map<String, String> allowedHelp, String defaultsTo,
217 void callback(bool value)]) { 222 void callback(bool value), bool multiValued]) {
Bob Nystrom 2012/08/09 23:08:40 How about: "multiValued" -> "allowMultiple"
gram 2012/08/09 23:47:00 Done.
218 _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, 223 _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo,
219 callback, isFlag: false); 224 callback, isFlag: false, multiValued: multiValued);
220 } 225 }
221 226
222 void _addOption(String name, String abbr, String help, List<String> allowed, 227 void _addOption(String name, String abbr, String help, List<String> allowed,
223 Map<String, String> allowedHelp, defaultsTo, 228 Map<String, String> allowedHelp, defaultsTo,
224 void callback(bool value), [bool isFlag, bool negatable = false]) { 229 void callback(bool value), [bool isFlag, bool negatable = false,
230 bool multiValued = false]) {
225 // Make sure the name isn't in use. 231 // Make sure the name isn't in use.
226 if (_options.containsKey(name)) { 232 if (_options.containsKey(name)) {
227 throw new IllegalArgumentException('Duplicate option "$name".'); 233 throw new IllegalArgumentException('Duplicate option "$name".');
228 } 234 }
229 235
230 // Make sure the abbreviation isn't too long or in use. 236 // Make sure the abbreviation isn't too long or in use.
231 if (abbr != null) { 237 if (abbr != null) {
232 if (abbr.length > 1) { 238 if (abbr.length > 1) {
233 throw new IllegalArgumentException( 239 throw new IllegalArgumentException(
234 'Abbreviation "$abbr" is longer than one character.'); 240 'Abbreviation "$abbr" is longer than one character.');
235 } 241 }
236 242
237 var existing = _findByAbbr(abbr); 243 var existing = _findByAbbr(abbr);
238 if (existing != null) { 244 if (existing != null) {
239 throw new IllegalArgumentException( 245 throw new IllegalArgumentException(
240 'Abbreviation "$abbr" is already used by "${existing.name}".'); 246 'Abbreviation "$abbr" is already used by "${existing.name}".');
241 } 247 }
242 } 248 }
243 249
244 _options[name] = new _Option(name, abbr, help, allowed, allowedHelp, 250 _options[name] = new _Option(name, abbr, help, allowed, allowedHelp,
245 defaultsTo, callback, isFlag: isFlag, negatable: negatable); 251 defaultsTo, callback, isFlag: isFlag, negatable: negatable,
252 multiValued: multiValued);
246 _optionNames.add(name); 253 _optionNames.add(name);
247 } 254 }
248 255
249 /** 256 /**
250 * Parses [args], a list of command-line arguments, matches them against the 257 * Parses [args], a list of command-line arguments, matches them against the
251 * flags and options defined by this parser, and returns the result. 258 * flags and options defined by this parser, and returns the result.
252 */ 259 */
253 ArgResults parse(List<String> args) { 260 ArgResults parse(List<String> args) {
254 _args = args; 261 _args = args;
255 _current = 0; 262 _current = 0;
256 var results = {}; 263 var results = {};
257 264
258 // Initialize flags to their defaults. 265 // Initialize flags to their defaults.
259 _options.forEach((name, option) { 266 _options.forEach((name, option) {
260 results[name] = option.defaultValue; 267 if (option.multiValued) {
268 results[name] = new List();
Bob Nystrom 2012/08/09 23:08:40 'new List()' => '[]'
gram 2012/08/09 23:47:00 Done.
269 } else {
270 results[name] = option.defaultValue;
271 }
261 }); 272 });
262 273
263 // Parse the args. 274 // Parse the args.
264 for (_current = 0; _current < args.length; _current++) { 275 for (_current = 0; _current < args.length; _current++) {
265 var arg = args[_current]; 276 var arg = args[_current];
266 277
267 if (arg == '--') { 278 if (arg == '--') {
268 // Reached the argument terminator, so stop here. 279 // Reached the argument terminator, so stop here.
269 _current++; 280 _current++;
270 break; 281 break;
271 } 282 }
272 283
273 // Try to parse the current argument as an option. Note that the order 284 // Try to parse the current argument as an option. Note that the order
274 // here matters. 285 // here matters.
275 if (_parseSoloOption(results)) continue; 286 if (_parseSoloOption(results)) continue;
276 if (_parseAbbreviation(results)) continue; 287 if (_parseAbbreviation(results)) continue;
277 if (_parseLongOption(results)) continue; 288 if (_parseLongOption(results)) continue;
278 289
279 // If we got here, the argument doesn't look like an option, so stop. 290 // If we got here, the argument doesn't look like an option, so stop.
280 break; 291 break;
281 } 292 }
282 293
283 // Invoke the callbacks. 294 // Set unspecified multivalued arguments to their default value,
295 // if any, and invoke the callbacks.
284 for (var name in _optionNames) { 296 for (var name in _optionNames) {
285 var option = _options[name]; 297 var option = _options[name];
298 if (option.multiValued &&
299 results[name].length == 0 &&
300 option.defaultValue != null) {
301 results[name].add(option.defaultValue);
302 }
286 if (option.callback != null) option.callback(results[name]); 303 if (option.callback != null) option.callback(results[name]);
287 } 304 }
288 305
289 // Add in the leftover arguments we didn't parse. 306 // Add in the leftover arguments we didn't parse.
290 return new ArgResults(results, 307 return new ArgResults(results,
291 _args.getRange(_current, _args.length - _current)); 308 _args.getRange(_current, _args.length - _current));
292 } 309 }
293 310
294 /** 311 /**
295 * Generates a string displaying usage information for the defined options. 312 * Generates a string displaying usage information for the defined options.
(...skipping 11 matching lines...) Expand all
307 if (!condition) throw new FormatException(message); 324 if (!condition) throw new FormatException(message);
308 } 325 }
309 326
310 /** Validates and stores [value] as the value for [option]. */ 327 /** Validates and stores [value] as the value for [option]. */
311 _setOption(Map results, _Option option, value) { 328 _setOption(Map results, _Option option, value) {
312 // See if it's one of the allowed values. 329 // See if it's one of the allowed values.
313 if (option.allowed != null) { 330 if (option.allowed != null) {
314 _validate(option.allowed.some((allow) => allow == value), 331 _validate(option.allowed.some((allow) => allow == value),
315 '"$value" is not an allowed value for option "${option.name}".'); 332 '"$value" is not an allowed value for option "${option.name}".');
316 } 333 }
317 334 if (option.multiValued) {
Bob Nystrom 2012/08/09 23:08:40 Nit, but can you add a blank line above this? I te
gram 2012/08/09 23:47:00 Done.
318 results[option.name] = value; 335 results[option.name].add(value);
336 } else {
337 results[option.name] = value;
338 }
319 } 339 }
320 340
321 /** 341 /**
322 * Pulls the value for [option] from the next argument in [_args] (where the 342 * Pulls the value for [option] from the next argument in [_args] (where the
323 * current option is at index [_current]. Validates that there is a valid 343 * current option is at index [_current]. Validates that there is a valid
324 * value there. 344 * value there.
325 */ 345 */
326 void _readNextArgAsValue(Map results, _Option option) { 346 void _readNextArgAsValue(Map results, _Option option) {
327 _current++; 347 _current++;
328 // Take the option argument from the next command line arg. 348 // Take the option argument from the next command line arg.
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
490 class _Option { 510 class _Option {
491 final String name; 511 final String name;
492 final String abbreviation; 512 final String abbreviation;
493 final List allowed; 513 final List allowed;
494 final defaultValue; 514 final defaultValue;
495 final Function callback; 515 final Function callback;
496 final String help; 516 final String help;
497 final Map<String, String> allowedHelp; 517 final Map<String, String> allowedHelp;
498 final bool isFlag; 518 final bool isFlag;
499 final bool negatable; 519 final bool negatable;
520 final bool multiValued;
500 521
501 _Option(this.name, this.abbreviation, this.help, this.allowed, 522 _Option(this.name, this.abbreviation, this.help, this.allowed,
502 this.allowedHelp, this.defaultValue, this.callback, [this.isFlag, 523 this.allowedHelp, this.defaultValue, this.callback, [this.isFlag,
503 this.negatable]); 524 this.negatable, this.multiValued]);
504 } 525 }
505 526
506 /** 527 /**
507 * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its 528 * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its
508 * defined options. Internally, it works like a tabular printer. The output is 529 * defined options. Internally, it works like a tabular printer. The output is
509 * divided into three horizontal columns, like so: 530 * divided into three horizontal columns, like so:
510 * 531 *
511 * -h, --help Prints the usage information 532 * -h, --help Prints the usage information
512 * | | | | 533 * | | | |
513 * 534 *
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
715 allowedBuffer.add(allowed); 736 allowedBuffer.add(allowed);
716 if (allowed == option.defaultValue) { 737 if (allowed == option.defaultValue) {
717 allowedBuffer.add(' (default)'); 738 allowedBuffer.add(' (default)');
718 } 739 }
719 first = false; 740 first = false;
720 } 741 }
721 allowedBuffer.add(']'); 742 allowedBuffer.add(']');
722 return allowedBuffer.toString(); 743 return allowedBuffer.toString();
723 } 744 }
724 } 745 }
OLDNEW
« no previous file with comments | « no previous file | tests/lib/args/args_test.dart » ('j') | tests/lib/args/args_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698