code: private CompositeExpression parseWildcardRange() {
          if (tokens.positiveLookahead(WILDCARD)) {
              tokens.consume();
              return gte(versionFor(0, 0, 0));
          }
  
          int major = intOf(consumeNextToken(NUMERIC).lexeme);
          consumeNextToken(DOT);
          if (tokens.positiveLookahead(WILDCARD)) {
              tokens.consume();
              return gte(versionFor(major)).and(lt(versionFor(major + 1)));
          }
  
          int minor = intOf(consumeNextToken(NUMERIC).lexeme);
          consumeNextToken(DOT);
          consumeNextToken(WILDCARD);
          return gte(versionFor(major, minor)).and(lt(versionFor(major, minor + 1)));
      }
docs: Parses the {@literal <wildcard-range>} non-terminal.
  
  <pre>
  {@literal
  <wildcard-range> ::= <wildcard>
  | <major> "." <wildcard>
  | <major> "." <minor> "." <wildcard>
  
  <wildcard> ::= "*" | "x" | "X"
  }
  </pre>
  
  @return the expression AST