blob: 57d94c1f24e1d196fe07532d2a03296cd672146f [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/expr/parse_special_identifier.h"
#include <ctype.h>
namespace zxdb {
namespace {
bool IsSpecialIdentifierChar(char c) { return isalnum(c) || c == '_'; }
} // namespace
Err ParseSpecialIdentifier(std::string_view input, size_t* cur, SpecialIdentifier* special,
std::string* contents, size_t* error_location) {
*special = SpecialIdentifier::kNone;
contents->clear();
if (*cur >= input.size() || input[*cur] != '$') {
*error_location = *cur;
return Err("This is not a special identifier."); // This is really an internal error.
}
// Skip over the '$'.
size_t special_name_begin = *cur;
++(*cur);
// Extract and move over the special name.
while (*cur < input.size() && IsSpecialIdentifierChar(input[*cur]))
++(*cur);
std::string_view special_name = input.substr(special_name_begin, *cur - special_name_begin);
*special = StringToSpecialIdentifier(special_name);
if (*special == SpecialIdentifier::kNone) {
*error_location = special_name_begin + 1; // Text after the "$".
return Err("The string '" + std::string(special_name) + "' is not a valid special identifier.");
}
// A paren following the special name is the contents.
if (*cur == input.size() || input[*cur] != '(') {
// No contents. There has to be a special name in this case to prevent us from getting confused
// by standalone "$".
if (*special == SpecialIdentifier::kEscaped) {
*error_location = *cur;
return Err("Expected special name or '(' for escaped input.");
}
return Err();
}
// Skip the paren.
size_t open_paren_index = *cur;
++(*cur);
int paren_depth = 0;
// Go through the contents of the (). Parens don't need escaping as long as they're matched, but
// can be escaped with backslashes.
while (*cur < input.size()) {
if (input[*cur] == '(') {
contents->push_back('(');
paren_depth++;
} else if (input[*cur] == ')') {
// Match with other open parens we've encountered, or it indicates the end.
if (paren_depth == 0) {
++(*cur); // Skip over closing paren.
return Err(); // Done.
}
paren_depth--;
contents->push_back(')');
} else if (input[*cur] == '\\') {
// Backslash escapes.
++(*cur); // Skip over backslash.
if (*cur == input.size())
break; // End-of-loop code handles this case.
char escaped_char = input[*cur];
if (escaped_char == '\\' || escaped_char == '(' || escaped_char == ')') {
// Only allow certain characters to be escaped.
contents->push_back(escaped_char);
} else {
// Everything else is an error.
*error_location = *cur;
return Err("Invalid backslash-escaped character in special identifier.");
}
} else {
// All other characters are literals.
contents->push_back(input[*cur]);
}
++(*cur);
}
// Error location indicates the opening paren which makes the error easier to understand.
*error_location = open_paren_index;
return Err("Unexpected end of input in special identifier to match.");
}
} // namespace zxdb