Add ArgParser.allowAnything(). (#71)

Closes #70
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f5c8c6..2a0f614 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.13.8
+
+* Add `new ArgParser.allowAnything()`. This allows any input, without parsing
+  any options.
+
 ## 0.13.7
 
 * Add explicit support for forwarding the value returned by `Command.run()` to
diff --git a/lib/src/allow_anything_parser.dart b/lib/src/allow_anything_parser.dart
new file mode 100644
index 0000000..450920c
--- /dev/null
+++ b/lib/src/allow_anything_parser.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'arg_parser.dart';
+import 'arg_results.dart';
+import 'option.dart';
+import 'parser.dart';
+
+/// An ArgParser that treats *all input* as non-option arguments.
+class AllowAnythingParser implements ArgParser {
+  Map<String, Option> get options => const {};
+  Map<String, ArgParser> get commands => const {};
+  bool get allowTrailingOptions => false;
+  bool get allowsAnything => true;
+
+  ArgParser addCommand(String name, [ArgParser parser]) {
+    throw new UnsupportedError(
+        "ArgParser.allowAnything().addCommands() isn't supported.");
+  }
+
+  void addFlag(String name,
+      {String abbr,
+      String help,
+      bool defaultsTo: false,
+      bool negatable: true,
+      void callback(bool value),
+      bool hide: false}) {
+    throw new UnsupportedError(
+        "ArgParser.allowAnything().addFlag() isn't supported.");
+  }
+
+  void addOption(String name,
+      {String abbr,
+      String help,
+      String valueHelp,
+      List<String> allowed,
+      Map<String, String> allowedHelp,
+      String defaultsTo,
+      void callback(value),
+      bool allowMultiple: false,
+      bool splitCommas,
+      bool hide: false}) {
+    throw new UnsupportedError(
+        "ArgParser.allowAnything().addOption() isn't supported.");
+  }
+
+  void addSeparator(String text) {
+    throw new UnsupportedError(
+        "ArgParser.allowAnything().addSeparator() isn't supported.");
+  }
+
+  ArgResults parse(List<String> args) =>
+      new Parser(null, this, args.toList()).parse();
+
+  String getUsage() => usage;
+
+  String get usage => "";
+
+  getDefault(String option) {
+    throw new ArgumentError('No option named $option');
+  }
+
+  Option findByAbbreviation(String abbr) => null;
+}
diff --git a/lib/src/arg_parser.dart b/lib/src/arg_parser.dart
index 44ceb64..69de95a 100644
--- a/lib/src/arg_parser.dart
+++ b/lib/src/arg_parser.dart
@@ -4,6 +4,7 @@
 
 import 'dart:collection';
 
+import 'allow_anything_parser.dart';
 import 'arg_results.dart';
 import 'option.dart';
 import 'parser.dart';
@@ -29,6 +30,10 @@
   /// arguments.
   final bool allowTrailingOptions;
 
+  /// Whether or not this parser treats unrecognized options as non-option
+  /// arguments.
+  bool get allowsAnything => false;
+
   /// Creates a new ArgParser.
   ///
   /// If [allowTrailingOptions] is set, the parser will continue parsing even
@@ -36,8 +41,15 @@
   /// This allows options to be specified after regular arguments. Defaults to
   /// `false`.
   factory ArgParser({bool allowTrailingOptions: false}) =>
-      new ArgParser._(<String, Option>{}, <String, ArgParser>{},
-          allowTrailingOptions: allowTrailingOptions);
+      new ArgParser._({}, {}, allowTrailingOptions: allowTrailingOptions);
+
+  /// Creates a new ArgParser that treats *all input* as non-option arguments.
+  ///
+  /// This is intended to allow arguments to be passed through to child
+  /// processes without needing to be redefined in the parent.
+  ///
+  /// Options may not be defined for this parser.
+  factory ArgParser.allowAnything() = AllowAnythingParser;
 
   ArgParser._(Map<String, Option> options, Map<String, ArgParser> commands,
       {bool allowTrailingOptions: false})
diff --git a/lib/src/parser.dart b/lib/src/parser.dart
index 82b74ce..c1bcc84 100644
--- a/lib/src/parser.dart
+++ b/lib/src/parser.dart
@@ -47,6 +47,11 @@
   /// Parses the arguments. This can only be called once.
   ArgResults parse() {
     var arguments = args.toList();
+    if (grammar.allowsAnything) {
+      return newArgResults(
+          grammar, const {}, commandName, null, arguments, arguments);
+    }
+
     ArgResults commandResults;
 
     // Parse the args.
diff --git a/pubspec.yaml b/pubspec.yaml
index e1a6b05..10531e4 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: args
-version: 0.13.7
+version: 0.13.8-dev
 author: "Dart Team <misc@dartlang.org>"
 homepage: https://github.com/dart-lang/args
 description: >
diff --git a/test/allow_anything_test.dart b/test/allow_anything_test.dart
new file mode 100644
index 0000000..7e229af
--- /dev/null
+++ b/test/allow_anything_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test/test.dart';
+
+import 'package:args/args.dart';
+
+import 'utils.dart';
+
+void main() {
+  group('new ArgParser.allowAnything()', () {
+    ArgParser parser;
+    setUp(() {
+      parser = new ArgParser.allowAnything();
+    });
+
+    test('exposes empty values', () {
+      expect(parser.options, isEmpty);
+      expect(parser.commands, isEmpty);
+      expect(parser.allowTrailingOptions, isFalse);
+      expect(parser.allowsAnything, isTrue);
+      expect(parser.usage, isEmpty);
+      expect(parser.getUsage(), isEmpty);
+      expect(parser.findByAbbreviation("a"), isNull);
+    });
+
+    test('mutation methods throw errors', () {
+      expect(() => parser.addCommand("command"), throwsUnsupportedError);
+      expect(() => parser.addFlag("flag"), throwsUnsupportedError);
+      expect(() => parser.addOption("option"), throwsUnsupportedError);
+      expect(() => parser.addSeparator("==="), throwsUnsupportedError);
+    });
+
+    test('getDefault() throws an error', () {
+      expect(() => parser.getDefault('option'), throwsArgumentError);
+    });
+
+    test('parses all values as rest arguments', () {
+      var results = parser.parse(['--foo', '-abc', '--', 'bar']);
+      expect(results.options, isEmpty);
+      expect(results.rest, equals(['--foo', '-abc', '--', 'bar']));
+      expect(results.arguments, equals(['--foo', '-abc', '--', 'bar']));
+      expect(results.command, isNull);
+      expect(results.name, isNull);
+    });
+
+    test('works as a subcommand', () {
+      var commandParser = new ArgParser()..addCommand('command', parser);
+      var results =
+          commandParser.parse(['command', '--foo', '-abc', '--', 'bar']);
+      expect(results.command.options, isEmpty);
+      expect(results.command.rest, equals(['--foo', '-abc', '--', 'bar']));
+      expect(results.command.arguments, equals(['--foo', '-abc', '--', 'bar']));
+      expect(results.command.name, equals('command'));
+    });
+  });
+}