in mysqlshdk/shellcore/provider_python.cc [278:434]
Provider_script::Chain Provider_python::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 '"':
case '\'':
p = span_python_string(s, 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 end of some other
case ' ': // or whitespace
case '\t':
case '\r':
chain.clear();
identifier.clear();
break;
case '#': { // comment starting
chain.clear();
identifier.clear();
// find end of line
size_t nl = s.find('\n', p);
if (nl == std::string::npos) {
p = end;
} else {
p = nl;
}
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;
}