Adds support for shadow piercing combinators

Fixes #23
diff --git a/lib/parser.dart b/lib/parser.dart
index 6b05c85..9b1b9ce 100644
--- a/lib/parser.dart
+++ b/lib/parser.dart
@@ -1244,13 +1244,30 @@
         combinatorType = TokenKind.COMBINATOR_PLUS;
         break;
       case TokenKind.GREATER:
+        // Parse > or >>>
         _eat(TokenKind.GREATER);
-        combinatorType = TokenKind.COMBINATOR_GREATER;
+        if (_maybeEat(TokenKind.GREATER)) {
+          _eat(TokenKind.GREATER);
+          combinatorType = TokenKind.COMBINATOR_DEEP;
+        } else {
+          combinatorType = TokenKind.COMBINATOR_GREATER;
+        }
         break;
       case TokenKind.TILDE:
         _eat(TokenKind.TILDE);
         combinatorType = TokenKind.COMBINATOR_TILDE;
         break;
+      case TokenKind.SLASH:
+        // Parse /deep/
+        _eat(TokenKind.SLASH);
+        var ate = _maybeEat(TokenKind.IDENTIFIER);
+        var tok = ate ? _previousToken : _peekToken;
+        if (!(ate && tok.text == 'deep')) {
+          _error('expected deep, but found ${tok.text}', tok.span);
+        }
+        _eat(TokenKind.SLASH);
+        combinatorType = TokenKind.COMBINATOR_DEEP;
+        break;
       case TokenKind.AMPERSAND:
         _eat(TokenKind.AMPERSAND);
         thisOperator = true;
diff --git a/lib/src/tokenkind.dart b/lib/src/tokenkind.dart
index 27ccb4b..2ad648a 100644
--- a/lib/src/tokenkind.dart
+++ b/lib/src/tokenkind.dart
@@ -100,8 +100,9 @@
   static const int COMBINATOR_PLUS = 515; // + combinator
   static const int COMBINATOR_GREATER = 516; // > combinator
   static const int COMBINATOR_TILDE = 517; // ~ combinator
+  static const int COMBINATOR_DEEP = 518; // /deep/ or >>> combinator
 
-  static const int UNARY_OP_NONE = 518; // No unary operator present.
+  static const int UNARY_OP_NONE = 519; // No unary operator present.
 
   // Attribute match types:
   static const int INCLUDES = 530; // '~='
diff --git a/lib/src/tree.dart b/lib/src/tree.dart
index 63c7301..a103679 100644
--- a/lib/src/tree.dart
+++ b/lib/src/tree.dart
@@ -120,12 +120,24 @@
   bool get isCombinatorTilde => combinator == TokenKind.COMBINATOR_TILDE;
   bool get isCombinatorDescendant =>
       combinator == TokenKind.COMBINATOR_DESCENDANT;
+  bool get isCombinatorDeep => combinator == TokenKind.COMBINATOR_DEEP;
 
-  String get _combinatorToString => isCombinatorDescendant
-      ? ' '
-      : isCombinatorPlus
-          ? ' + '
-          : isCombinatorGreater ? ' > ' : isCombinatorTilde ? ' ~ ' : '';
+  String get _combinatorToString {
+    switch (combinator) {
+      case TokenKind.COMBINATOR_DEEP:
+        return ' /deep/ ';
+      case TokenKind.COMBINATOR_DESCENDANT:
+        return ' ';
+      case TokenKind.COMBINATOR_GREATER:
+        return ' > ';
+      case TokenKind.COMBINATOR_PLUS:
+        return ' + ';
+      case TokenKind.COMBINATOR_TILDE:
+        return ' ~ ';
+      default:
+        return '';
+    }
+  }
 
   SimpleSelectorSequence clone() =>
       new SimpleSelectorSequence(simpleSelector, span, combinator);
diff --git a/lib/src/tree_printer.dart b/lib/src/tree_printer.dart
index 97c6acc..94ca193 100644
--- a/lib/src/tree_printer.dart
+++ b/lib/src/tree_printer.dart
@@ -271,6 +271,8 @@
       output.writeValue('combinator', ">");
     } else if (node.isCombinatorTilde) {
       output.writeValue('combinator', "~");
+    } else if (node.isCombinatorDeep) {
+      output.writeValue('combinator', '/deep/');
     } else {
       output.writeValue('combinator', "ERROR UNKNOWN");
     }
diff --git a/test/selector_test.dart b/test/selector_test.dart
index 87fb60a..eb2bef8 100644
--- a/test/selector_test.dart
+++ b/test/selector_test.dart
@@ -58,6 +58,14 @@
   selectorAst = selector(':host-context(.foo)', errors: errors..clear());
   expect(errors.isEmpty, true, reason: errors.toString());
   expect(compactOuptut(selectorAst), ':host-context(.foo)');
+
+  selectorAst = selector('.a /deep/ .b', errors: errors..clear());
+  expect(errors.isEmpty, true, reason: errors.toString());
+  expect(compactOuptut(selectorAst), '.a /deep/ .b');
+
+  selectorAst = selector('.x >>> .y', errors: errors..clear());
+  expect(errors.isEmpty, true, reason: errors.toString());
+  expect(compactOuptut(selectorAst), '.x /deep/ .y');
 }
 
 // TODO(terry): Move this failure case to a failure_test.dart when the analyzer