public String toString(Token t) {
   if (t.getKind().hasPayload()) {
     return t.stringValue();
   } else {
     return t.kind.toString().toLowerCase();
   }
 }
 /**
  * Eat an identifier, possibly qualified (meaning that it is dotted). TODO AndyC Could create
  * complete identifiers (a.b.c) here rather than a sequence of them? (a, b, c)
  */
 private SpelNodeImpl eatPossiblyQualifiedId() {
   LinkedList<SpelNodeImpl> qualifiedIdPieces = new LinkedList<SpelNodeImpl>();
   Token node = peekToken();
   while (isValidQualifiedId(node)) {
     nextToken();
     if (node.kind != TokenKind.DOT) {
       qualifiedIdPieces.add(new Identifier(node.stringValue(), toPos(node)));
     }
     node = peekToken();
   }
   if (qualifiedIdPieces.isEmpty()) {
     if (node == null) {
       raiseInternalException(expressionString.length(), SpelMessage.OOD);
     }
     raiseInternalException(
         node.startpos,
         SpelMessage.NOT_EXPECTED_TOKEN,
         "qualified ID",
         node.getKind().toString().toLowerCase());
   }
   int pos =
       toPos(
           qualifiedIdPieces.getFirst().getStartPosition(),
           qualifiedIdPieces.getLast().getEndPosition());
   return new QualifiedIdentifier(
       pos, qualifiedIdPieces.toArray(new SpelNodeImpl[qualifiedIdPieces.size()]));
 }
 private boolean peekIdentifierToken(String identifierString) {
   if (!moreTokens()) {
     return false;
   }
   Token t = peekToken();
   return t.kind == TokenKind.IDENTIFIER && t.stringValue().equalsIgnoreCase(identifierString);
 }
 private boolean isValidQualifiedId(Token node) {
   if (node == null || node.kind == TokenKind.LITERAL_STRING) {
     return false;
   }
   if (node.kind == TokenKind.DOT || node.kind == TokenKind.IDENTIFIER) {
     return true;
   }
   String value = node.stringValue();
   return StringUtils.hasLength(value) && VALID_QUALIFIED_ID_PATTERN.matcher(value).matches();
 }
 private boolean maybeEatNullReference() {
   if (peekToken(TokenKind.IDENTIFIER)) {
     Token nullToken = peekToken();
     if (!nullToken.stringValue().equalsIgnoreCase("null")) {
       return false;
     }
     nextToken();
     constructedNodes.push(new NullLiteral(toPos(nullToken)));
     return true;
   }
   return false;
 }
 // relationalOperator
 // : EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN
 // | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES
 private Token maybeEatRelationalOperator() {
   Token t = peekToken();
   if (t == null) {
     return null;
   }
   if (t.isNumericRelationalOperator()) {
     return t;
   }
   if (t.isIdentifier()) {
     String idString = t.stringValue();
     if (idString.equalsIgnoreCase("instanceof")) {
       return t.asInstanceOfToken();
     } else if (idString.equalsIgnoreCase("matches")) {
       return t.asMatchesToken();
     } else if (idString.equalsIgnoreCase("between")) {
       return t.asBetweenToken();
     }
   }
   return null;
 }
 private boolean maybeEatTypeReference() {
   if (peekToken(TokenKind.IDENTIFIER)) {
     Token typeName = peekToken();
     if (!typeName.stringValue().equals("T")) {
       return false;
     }
     nextToken();
     eatToken(TokenKind.LPAREN);
     SpelNodeImpl node = eatPossiblyQualifiedId();
     // dotted qualified id
     // Are there array dimensions?
     int dims = 0;
     while (peekToken(TokenKind.LSQUARE, true)) {
       eatToken(TokenKind.RSQUARE);
       dims++;
     }
     eatToken(TokenKind.RPAREN);
     constructedNodes.push(new TypeReference(toPos(typeName), node, dims));
     return true;
   }
   return false;
 }
  // parse: @beanname @'bean.name'
  // quoted if dotted
  private boolean maybeEatBeanReference() {
    if (peekToken(TokenKind.BEAN_REF)) {
      Token beanRefToken = nextToken();
      Token beanNameToken = null;
      String beanname = null;
      if (peekToken(TokenKind.IDENTIFIER)) {
        beanNameToken = eatToken(TokenKind.IDENTIFIER);
        beanname = beanNameToken.data;
      } else if (peekToken(TokenKind.LITERAL_STRING)) {
        beanNameToken = eatToken(TokenKind.LITERAL_STRING);
        beanname = beanNameToken.stringValue();
        beanname = beanname.substring(1, beanname.length() - 1);
      } else {
        raiseInternalException(beanRefToken.startpos, SpelMessage.INVALID_BEAN_REFERENCE);
      }

      BeanReference beanReference = new BeanReference(toPos(beanNameToken), beanname);
      constructedNodes.push(beanReference);
      return true;
    }
    return false;
  }