in mysqlshdk/shellcore/provider_polyglot.cc [172:352]
Provider_script::Chain Provider_polyglot::parse_until(const std::string &s,
size_t *pos,
int close_char,
size_t *chain_start_pos) {
size_t end = s.length();
size_t &p = *pos;
Provider_script::Chain chain;
std::string identifier;
while (p < end && !chain.invalid()) {
switch (s[p]) {
case '"':
p = mysqlshdk::utils::span_quoted_string_dq(s, p);
if (p == std::string::npos) {
chain.invalidate();
return chain;
}
--p;
break;
case '\'':
p = mysqlshdk::utils::span_quoted_string_sq(s, p);
if (p == std::string::npos) {
chain.invalidate();
return chain;
}
--p;
break;
case '{':
case '[':
case '(': {
int closer = 0;
switch (s[p]) {
case '[':
closer = ']';
break;
case '{':
closer = '}';
break;
case '(':
closer = ')';
break;
}
++p;
size_t inner_pos = p;
Provider_script::Chain inner(parse_until(s, &p, closer, &inner_pos));
// if the inner block was not closed, then it's the last one
if (p == end || inner.invalid()) {
*chain_start_pos = inner_pos;
return inner;
}
// otherwise, throw it away, consider it a method call/array/map
// and continue
if (!identifier.empty()) {
if (closer == '}') {
// this was a dict
chain.clear();
} else if (closer == ']' && identifier.empty()) {
// this was an array
chain.clear();
} else if (closer == ')') {
if (chain.add_method(identifier))
*chain_start_pos = p - identifier.length();
} else {
if (chain.add_variable(identifier))
*chain_start_pos = p - identifier.length();
}
identifier.clear();
}
break;
}
case '}':
case ')':
case ']':
if (s[p] == close_char) return chain;
chain.clear();
identifier.clear();
// unexpected closing thingy, probably bad syntax, but we don't care
break;
case '\n': // a new line is a hard break on the previous part of a stmt
case ' ': // or whitespace
case '\t':
case '\r':
chain.clear();
identifier.clear();
break;
case '/': // comment starting
if (p + 1 < end) {
if (s[p + 1] == '/') {
// skip until EOL
size_t nl = s.find('\n', p);
if (nl == std::string::npos) {
chain.clear();
identifier.clear();
p = end;
} else {
p = nl;
}
} else if (s[p + 1] == '*') {
size_t eoc = mysqlshdk::utils::span_cstyle_comment(s, p);
if (eoc == std::string::npos) {
chain.invalidate();
return chain;
} else {
p = eoc;
}
}
}
break;
case '\\': { // escape outside a string means line continuation..
// skip util end of line
size_t nl = s.find('\n', p);
if (nl == std::string::npos) {
chain.clear();
identifier.clear();
p = end;
} else {
p = nl;
}
break;
}
case '.':
if (!identifier.empty()) {
if (chain.add_variable(identifier))
*chain_start_pos = p - identifier.length();
} else {
// consecutive dots or dot at beginning = syntax error
if (p == 0 || chain.empty() || s[p - 1] == '.') {
chain.invalidate();
return chain;
}
}
identifier.clear();
chain.add_dot();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (!identifier.empty()) {
identifier.push_back(s[p]);
} else { // a number can't appear in a chain, outside an id or param
chain.clear();
}
break;
case '_':
identifier.push_back(s[p]);
break;
default:
// if we see any identifier-like char, we're in an identifier
// anything else is a break
if (isalpha(s[p])) {
identifier.push_back(s[p]);
} else {
// garbage...
chain.clear();
identifier.clear();
}
break;
}
++p;
}
if (chain.add_variable(identifier))
*chain_start_pos = p - identifier.length();
return chain;
}