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

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') | no next file with comments »
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; for example:
128 *
129 * var parser = new ArgParser();
130 * parser.addOption('mode');
131 * var results = parser.parse(['--mode', 'on', '--mode', 'off']);
132 * print(results['mode']); // prints 'off'
133 *
134 * If you need multiple values, set the [allowMultiple] flag. In that
135 * case the option can occur multiple times and when parsing arguments a
136 * List of values will be returned:
137 *
138 * var parser = new ArgParser();
139 * parser.addOption('mode', allowMultiple: true);
140 * var results = parser.parse(['--mode', 'on', '--mode', 'off']);
141 * print(results['mode']); // prints '[on, off]'
142 *
126 * ## Usage ## 143 * ## Usage ##
127 * 144 *
128 * This library can also be used to automatically generate nice usage help 145 * 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 146 * 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 147 * will also want to provide some help text when you create your options. To
131 * define help text for the entire option, do: 148 * define help text for the entire option, do:
132 * 149 *
133 * parser.addOption('mode', help: 'The compiler configuration', 150 * parser.addOption('mode', help: 'The compiler configuration',
134 * allowed: ['debug', 'release']); 151 * allowed: ['debug', 'release']);
135 * parser.addFlag('verbose', help: 'Show additional diagnostic info'); 152 * parser.addFlag('verbose', help: 'Show additional diagnostic info');
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 } 224 }
208 225
209 /** 226 /**
210 * Defines a value-taking option. Throws an [IllegalArgumentException] if: 227 * Defines a value-taking option. Throws an [IllegalArgumentException] if:
211 * 228 *
212 * * There is already an option with name [name]. 229 * * There is already an option with name [name].
213 * * There is already an option using abbreviation [abbr]. 230 * * There is already an option using abbreviation [abbr].
214 */ 231 */
215 void addOption(String name, [String abbr, String help, List<String> allowed, 232 void addOption(String name, [String abbr, String help, List<String> allowed,
216 Map<String, String> allowedHelp, String defaultsTo, 233 Map<String, String> allowedHelp, String defaultsTo,
217 void callback(bool value)]) { 234 void callback(bool value), bool allowMultiple = false]) {
218 _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, 235 _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo,
219 callback, isFlag: false); 236 callback, isFlag: false, allowMultiple: allowMultiple);
220 } 237 }
221 238
222 void _addOption(String name, String abbr, String help, List<String> allowed, 239 void _addOption(String name, String abbr, String help, List<String> allowed,
223 Map<String, String> allowedHelp, defaultsTo, 240 Map<String, String> allowedHelp, defaultsTo,
224 void callback(bool value), [bool isFlag, bool negatable = false]) { 241 void callback(bool value), [bool isFlag, bool negatable = false,
242 bool allowMultiple = false]) {
225 // Make sure the name isn't in use. 243 // Make sure the name isn't in use.
226 if (_options.containsKey(name)) { 244 if (_options.containsKey(name)) {
227 throw new IllegalArgumentException('Duplicate option "$name".'); 245 throw new IllegalArgumentException('Duplicate option "$name".');
228 } 246 }
229 247
230 // Make sure the abbreviation isn't too long or in use. 248 // Make sure the abbreviation isn't too long or in use.
231 if (abbr != null) { 249 if (abbr != null) {
232 if (abbr.length > 1) { 250 if (abbr.length > 1) {
233 throw new IllegalArgumentException( 251 throw new IllegalArgumentException(
234 'Abbreviation "$abbr" is longer than one character.'); 252 'Abbreviation "$abbr" is longer than one character.');
235 } 253 }
236 254
237 var existing = _findByAbbr(abbr); 255 var existing = _findByAbbr(abbr);
238 if (existing != null) { 256 if (existing != null) {
239 throw new IllegalArgumentException( 257 throw new IllegalArgumentException(
240 'Abbreviation "$abbr" is already used by "${existing.name}".'); 258 'Abbreviation "$abbr" is already used by "${existing.name}".');
241 } 259 }
242 } 260 }
243 261
244 _options[name] = new _Option(name, abbr, help, allowed, allowedHelp, 262 _options[name] = new _Option(name, abbr, help, allowed, allowedHelp,
245 defaultsTo, callback, isFlag: isFlag, negatable: negatable); 263 defaultsTo, callback, isFlag: isFlag, negatable: negatable,
264 allowMultiple: allowMultiple);
246 _optionNames.add(name); 265 _optionNames.add(name);
247 } 266 }
248 267
249 /** 268 /**
250 * Parses [args], a list of command-line arguments, matches them against the 269 * Parses [args], a list of command-line arguments, matches them against the
251 * flags and options defined by this parser, and returns the result. 270 * flags and options defined by this parser, and returns the result.
252 */ 271 */
253 ArgResults parse(List<String> args) { 272 ArgResults parse(List<String> args) {
254 _args = args; 273 _args = args;
255 _current = 0; 274 _current = 0;
256 var results = {}; 275 var results = {};
257 276
258 // Initialize flags to their defaults. 277 // Initialize flags to their defaults.
259 _options.forEach((name, option) { 278 _options.forEach((name, option) {
260 results[name] = option.defaultValue; 279 if (option.allowMultiple) {
280 results[name] = [];
281 } else {
282 results[name] = option.defaultValue;
283 }
261 }); 284 });
262 285
263 // Parse the args. 286 // Parse the args.
264 for (_current = 0; _current < args.length; _current++) { 287 for (_current = 0; _current < args.length; _current++) {
265 var arg = args[_current]; 288 var arg = args[_current];
266 289
267 if (arg == '--') { 290 if (arg == '--') {
268 // Reached the argument terminator, so stop here. 291 // Reached the argument terminator, so stop here.
269 _current++; 292 _current++;
270 break; 293 break;
271 } 294 }
272 295
273 // Try to parse the current argument as an option. Note that the order 296 // Try to parse the current argument as an option. Note that the order
274 // here matters. 297 // here matters.
275 if (_parseSoloOption(results)) continue; 298 if (_parseSoloOption(results)) continue;
276 if (_parseAbbreviation(results)) continue; 299 if (_parseAbbreviation(results)) continue;
277 if (_parseLongOption(results)) continue; 300 if (_parseLongOption(results)) continue;
278 301
279 // If we got here, the argument doesn't look like an option, so stop. 302 // If we got here, the argument doesn't look like an option, so stop.
280 break; 303 break;
281 } 304 }
282 305
283 // Invoke the callbacks. 306 // Set unspecified multivalued arguments to their default value,
307 // if any, and invoke the callbacks.
284 for (var name in _optionNames) { 308 for (var name in _optionNames) {
285 var option = _options[name]; 309 var option = _options[name];
310 if (option.allowMultiple &&
311 results[name].length == 0 &&
312 option.defaultValue != null) {
313 results[name].add(option.defaultValue);
314 }
286 if (option.callback != null) option.callback(results[name]); 315 if (option.callback != null) option.callback(results[name]);
287 } 316 }
288 317
289 // Add in the leftover arguments we didn't parse. 318 // Add in the leftover arguments we didn't parse.
290 return new ArgResults(results, 319 return new ArgResults(results,
291 _args.getRange(_current, _args.length - _current)); 320 _args.getRange(_current, _args.length - _current));
292 } 321 }
293 322
294 /** 323 /**
295 * Generates a string displaying usage information for the defined options. 324 * Generates a string displaying usage information for the defined options.
(...skipping 12 matching lines...) Expand all
308 } 337 }
309 338
310 /** Validates and stores [value] as the value for [option]. */ 339 /** Validates and stores [value] as the value for [option]. */
311 _setOption(Map results, _Option option, value) { 340 _setOption(Map results, _Option option, value) {
312 // See if it's one of the allowed values. 341 // See if it's one of the allowed values.
313 if (option.allowed != null) { 342 if (option.allowed != null) {
314 _validate(option.allowed.some((allow) => allow == value), 343 _validate(option.allowed.some((allow) => allow == value),
315 '"$value" is not an allowed value for option "${option.name}".'); 344 '"$value" is not an allowed value for option "${option.name}".');
316 } 345 }
317 346
318 results[option.name] = value; 347 if (option.allowMultiple) {
348 results[option.name].add(value);
349 } else {
350 results[option.name] = value;
351 }
319 } 352 }
320 353
321 /** 354 /**
322 * Pulls the value for [option] from the next argument in [_args] (where the 355 * 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 356 * current option is at index [_current]. Validates that there is a valid
324 * value there. 357 * value there.
325 */ 358 */
326 void _readNextArgAsValue(Map results, _Option option) { 359 void _readNextArgAsValue(Map results, _Option option) {
327 _current++; 360 _current++;
328 // Take the option argument from the next command line arg. 361 // 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 { 523 class _Option {
491 final String name; 524 final String name;
492 final String abbreviation; 525 final String abbreviation;
493 final List allowed; 526 final List allowed;
494 final defaultValue; 527 final defaultValue;
495 final Function callback; 528 final Function callback;
496 final String help; 529 final String help;
497 final Map<String, String> allowedHelp; 530 final Map<String, String> allowedHelp;
498 final bool isFlag; 531 final bool isFlag;
499 final bool negatable; 532 final bool negatable;
533 final bool allowMultiple;
500 534
501 _Option(this.name, this.abbreviation, this.help, this.allowed, 535 _Option(this.name, this.abbreviation, this.help, this.allowed,
502 this.allowedHelp, this.defaultValue, this.callback, [this.isFlag, 536 this.allowedHelp, this.defaultValue, this.callback, [this.isFlag,
503 this.negatable]); 537 this.negatable, this.allowMultiple = false]);
504 } 538 }
505 539
506 /** 540 /**
507 * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its 541 * 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 542 * defined options. Internally, it works like a tabular printer. The output is
509 * divided into three horizontal columns, like so: 543 * divided into three horizontal columns, like so:
510 * 544 *
511 * -h, --help Prints the usage information 545 * -h, --help Prints the usage information
512 * | | | | 546 * | | | |
513 * 547 *
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
715 allowedBuffer.add(allowed); 749 allowedBuffer.add(allowed);
716 if (allowed == option.defaultValue) { 750 if (allowed == option.defaultValue) {
717 allowedBuffer.add(' (default)'); 751 allowedBuffer.add(' (default)');
718 } 752 }
719 first = false; 753 first = false;
720 } 754 }
721 allowedBuffer.add(']'); 755 allowedBuffer.add(']');
722 return allowedBuffer.toString(); 756 return allowedBuffer.toString();
723 } 757 }
724 } 758 }
OLDNEW
« no previous file with comments | « no previous file | tests/lib/args/args_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698