in src/main/user-impl/java/com/mysql/cj/xdevapi/ExprParser.java [277:480]
void lex() {
for (int i = 0; i < this.string.length(); ++i) {
int start = i; // for routines that consume more than one char
char c = this.string.charAt(i);
if (Character.isWhitespace(c)) {
// ignore
} else if (Character.isDigit(c)) {
i = lexNumber(i);
} else if (!(c == '_' || Character.isUnicodeIdentifierStart(c))) {
// non-identifier, e.g. operator or quoted literal
switch (c) {
case ':':
this.tokens.add(new Token(TokenType.COLON, c));
break;
case '+':
this.tokens.add(new Token(TokenType.PLUS, c));
break;
case '-':
if (nextCharEquals(i, '>')) {
i++;
this.tokens.add(new Token(TokenType.COLDOCPATH, "->"));
} else {
this.tokens.add(new Token(TokenType.MINUS, c));
}
break;
case '*':
if (nextCharEquals(i, '*')) {
i++;
this.tokens.add(new Token(TokenType.DOUBLESTAR, "**"));
} else {
this.tokens.add(new Token(TokenType.STAR, c));
}
break;
case '/':
this.tokens.add(new Token(TokenType.SLASH, c));
break;
case '$':
this.tokens.add(new Token(TokenType.DOLLAR, c));
break;
case '%':
this.tokens.add(new Token(TokenType.MOD, c));
break;
case '=':
if (nextCharEquals(i, '=')) {
i++;
}
this.tokens.add(new Token(TokenType.EQ, "=="));
break;
case '&':
if (nextCharEquals(i, '&')) {
i++;
this.tokens.add(new Token(TokenType.ANDAND, "&&"));
} else {
this.tokens.add(new Token(TokenType.BITAND, c));
}
break;
case '|':
if (nextCharEquals(i, '|')) {
i++;
this.tokens.add(new Token(TokenType.OROR, "||"));
} else {
this.tokens.add(new Token(TokenType.BITOR, c));
}
break;
case '^':
this.tokens.add(new Token(TokenType.BITXOR, c));
break;
case '(':
this.tokens.add(new Token(TokenType.LPAREN, c));
break;
case ')':
this.tokens.add(new Token(TokenType.RPAREN, c));
break;
case '[':
this.tokens.add(new Token(TokenType.LSQBRACKET, c));
break;
case ']':
this.tokens.add(new Token(TokenType.RSQBRACKET, c));
break;
case '{':
this.tokens.add(new Token(TokenType.LCURLY, c));
break;
case '}':
this.tokens.add(new Token(TokenType.RCURLY, c));
break;
case '~':
this.tokens.add(new Token(TokenType.NEG, c));
break;
case ',':
this.tokens.add(new Token(TokenType.COMMA, c));
break;
case '!':
if (nextCharEquals(i, '=')) {
i++;
this.tokens.add(new Token(TokenType.NE, "!="));
} else {
this.tokens.add(new Token(TokenType.BANG, c));
}
break;
case '?':
this.tokens.add(new Token(TokenType.EROTEME, c));
break;
case '<':
if (nextCharEquals(i, '<')) {
i++;
this.tokens.add(new Token(TokenType.LSHIFT, "<<"));
} else if (nextCharEquals(i, '=')) {
i++;
this.tokens.add(new Token(TokenType.LE, "<="));
} else {
this.tokens.add(new Token(TokenType.LT, c));
}
break;
case '>':
if (nextCharEquals(i, '>')) {
i++;
this.tokens.add(new Token(TokenType.RSHIFT, ">>"));
} else if (nextCharEquals(i, '=')) {
i++;
this.tokens.add(new Token(TokenType.GE, ">="));
} else {
this.tokens.add(new Token(TokenType.GT, c));
}
break;
case '.':
if (nextCharEquals(i, '*')) {
i++;
this.tokens.add(new Token(TokenType.DOTSTAR, ".*"));
} else if (i + 1 < this.string.length() && Character.isDigit(this.string.charAt(i + 1))) {
i = lexNumber(i);
} else {
this.tokens.add(new Token(TokenType.DOT, c));
}
break;
case '"':
case '\'':
case '`':
char quoteChar = c;
StringBuilder val = new StringBuilder();
try {
boolean escapeNextChar = false;
for (c = this.string.charAt(++i); c != quoteChar || escapeNextChar
|| i + 1 < this.string.length() && this.string.charAt(i + 1) == quoteChar; c = this.string.charAt(++i)) {
if (escapeNextChar) {
if (escapeChars.containsKey(c)) {
val.append(escapeChars.get(c));
} else if (c == 'u') {
// \\u[4 hex digits] represents a unicode code point (ISO/IEC 10646)
char[] buf = new char[4];
this.string.getChars(++i, i + 4, buf, 0);
String hexCodePoint = String.valueOf(buf);
try {
val.append((char) Integer.parseInt(hexCodePoint, 16));
} catch (NumberFormatException e) {
throw new WrongArgumentException("Invalid Unicode code point '" + hexCodePoint + "'");
}
i += 3;
} else {
val.append('\\').append(c);
}
escapeNextChar = false;
} else if (c == '\\' || c == quoteChar) { // Escape sequence or two consecutive quotes
escapeNextChar = true;
} else {
val.append(c);
}
}
if (escapeNextChar) {
throw new WrongArgumentException("Unterminated escape sequence at " + i);
}
} catch (StringIndexOutOfBoundsException ex) {
throw new WrongArgumentException("Unterminated string starting at " + start);
}
this.tokens.add(new Token(quoteChar == '`' ? TokenType.IDENT : TokenType.LSTRING, val.toString()));
break;
default:
throw new WrongArgumentException("Can't parse at position " + i);
}
} else {
// otherwise, it's an identifier
for (; i < this.string.length() && Character.isUnicodeIdentifierPart(this.string.charAt(i)); ++i) {
}
String val = this.string.substring(start, i);
String valLower = val.toLowerCase();
if (i < this.string.length()) {
// last char, this logic is artifact of the preceding loop
--i;
}
if (reservedWords.containsKey(valLower)) {
// Map operator names to values the server understands
if ("and".equals(valLower)) {
this.tokens.add(new Token(reservedWords.get(valLower), "&&"));
} else if ("or".equals(valLower)) {
this.tokens.add(new Token(reservedWords.get(valLower), "||"));
} else {
// we case-normalize reserved words
this.tokens.add(new Token(reservedWords.get(valLower), valLower));
}
} else {
this.tokens.add(new Token(TokenType.IDENT, val));
}
}
}
}