code: private CompositeExpression parseCaretRange() {
          consumeNextToken(CARET);
          int major = intOf(consumeNextToken(NUMERIC).lexeme);
          if (!tokens.positiveLookahead(DOT)) {
              return gte(versionFor(major)).and(lt(versionFor(major + 1)));
          }
          consumeNextToken(DOT);
          int minor = intOf(consumeNextToken(NUMERIC).lexeme);
          if (!tokens.positiveLookahead(DOT)) {
              Version lower = versionFor(major, minor);
              Version upper = major > 0 ? lower.incrementMajorVersion() : lower.incrementMinorVersion();
              return gte(lower).and(lt(upper));
          }
          consumeNextToken(DOT);
          int patch = intOf(consumeNextToken(NUMERIC).lexeme);
          Version version = versionFor(major, minor, patch);
          CompositeExpression gte = gte(version);
          if (major > 0) {
              return gte.and(lt(version.incrementMajorVersion()));
          } else if (minor > 0) {
              return gte.and(lt(version.incrementMinorVersion()));
          } else if (patch > 0) {
              return gte.and(lt(version.incrementPatchVersion()));
          }
          return eq(version);
      }
docs: Parses the {@literal <caret-range>} non-terminal.
  
  <pre>
  {@literal
  <caret-range> ::= "^" <version>
  }
  </pre>
  
  @return the expression AST