parser: support recursive $-expansions
This commit is contained in:
parent
650c6140bd
commit
0c6bf5a7a9
2 changed files with 73 additions and 26 deletions
|
|
@ -455,38 +455,53 @@ size_t Parser::extractToEol(Reader& reader, std::string* dst)
|
||||||
std::string Parser::expandDollarVars(const SourceRange& range, absl::string_view src)
|
std::string Parser::expandDollarVars(const SourceRange& range, absl::string_view src)
|
||||||
{
|
{
|
||||||
std::string dst;
|
std::string dst;
|
||||||
|
std::string srcbuf; // temporary for retries when recursive
|
||||||
|
std::string name; // temporary for variable name
|
||||||
|
bool keepExpanding = true;
|
||||||
|
|
||||||
dst.reserve(2 * src.size());
|
dst.reserve(2 * src.size());
|
||||||
|
name.reserve(64);
|
||||||
|
|
||||||
size_t i = 0;
|
while (keepExpanding) {
|
||||||
size_t n = src.size();
|
size_t i = 0;
|
||||||
while (i < n) {
|
size_t n = src.size();
|
||||||
char c = src[i++];
|
size_t numExpansions = 0;
|
||||||
|
while (i < n) {
|
||||||
|
char c = src[i++];
|
||||||
|
|
||||||
if (c != '$')
|
if (c != '$')
|
||||||
dst.push_back(c);
|
dst.push_back(c);
|
||||||
else {
|
else {
|
||||||
std::string name;
|
++numExpansions;
|
||||||
name.reserve(64);
|
name.clear();
|
||||||
|
|
||||||
// ARIA: we will accumulate any chars after $, until this is the
|
// ARIA: we will accumulate any chars after $, until this is the
|
||||||
// name of a known variable
|
// name of a known variable
|
||||||
auto def = _currentDefinitions.end();
|
auto def = _currentDefinitions.end();
|
||||||
while (i < n && isIdentifierChar(src[i]) && def == _currentDefinitions.end()) {
|
while (i < n && isIdentifierChar(src[i]) && def == _currentDefinitions.end()) {
|
||||||
name.push_back(src[i++]);
|
name.push_back(src[i++]);
|
||||||
def = _currentDefinitions.find(name);
|
def = _currentDefinitions.find(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.empty()) {
|
||||||
|
emitWarning(range, "Expected variable name after $.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def == _currentDefinitions.end()) {
|
||||||
|
emitWarning(range, "The variable `" + name + "` is not defined.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.append(def->second);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (name.empty()) {
|
keepExpanding = numExpansions > 0;
|
||||||
emitWarning(range, "Expected variable name after $.");
|
if (keepExpanding) {
|
||||||
continue;
|
srcbuf = dst;
|
||||||
}
|
src = srcbuf;
|
||||||
|
dst.clear();
|
||||||
if (def == _currentDefinitions.end()) {
|
|
||||||
emitWarning(range, "The variable `" + name + "` is not defined.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.append(def->second);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -589,3 +589,35 @@ R"(#define $a foo #define $b bar <region> sample=$a-$b.wav
|
||||||
REQUIRE(mock.fullBlockHeaders == expectedHeaders);
|
REQUIRE(mock.fullBlockHeaders == expectedHeaders);
|
||||||
REQUIRE(mock.fullBlockMembers == expectedMembers);
|
REQUIRE(mock.fullBlockMembers == expectedMembers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Parsing] Recursive expansion")
|
||||||
|
{
|
||||||
|
sfz::Parser parser;
|
||||||
|
ParsingMocker mock;
|
||||||
|
parser.setListener(&mock);
|
||||||
|
parser.parseString("/recursiveExpansion.sfz",
|
||||||
|
R"(#define $B foo-$A-baz
|
||||||
|
#define $A bar
|
||||||
|
<region> sample=$B.wav)");
|
||||||
|
|
||||||
|
std::vector<std::vector<sfz::Opcode>> expectedMembers = {
|
||||||
|
{{"sample", "foo-bar-baz.wav"}},
|
||||||
|
};
|
||||||
|
std::vector<std::string> expectedHeaders = {
|
||||||
|
"region"
|
||||||
|
};
|
||||||
|
std::vector<sfz::Opcode> expectedOpcodes;
|
||||||
|
|
||||||
|
for (auto& members: expectedMembers)
|
||||||
|
for (auto& opcode: members)
|
||||||
|
expectedOpcodes.push_back(opcode);
|
||||||
|
|
||||||
|
REQUIRE(mock.beginnings == 1);
|
||||||
|
REQUIRE(mock.endings == 1);
|
||||||
|
REQUIRE(mock.errors.empty());
|
||||||
|
REQUIRE(mock.warnings.empty());
|
||||||
|
REQUIRE(mock.opcodes == expectedOpcodes);
|
||||||
|
REQUIRE(mock.headers == expectedHeaders);
|
||||||
|
REQUIRE(mock.fullBlockHeaders == expectedHeaders);
|
||||||
|
REQUIRE(mock.fullBlockMembers == expectedMembers);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue