diff --git a/src/sfizz/parser/Parser.cpp b/src/sfizz/parser/Parser.cpp index 51eab71e..a08d2bcc 100644 --- a/src/sfizz/parser/Parser.cpp +++ b/src/sfizz/parser/Parser.cpp @@ -285,36 +285,47 @@ void Parser::processOpcode() std::string valueRaw; extractToEol(reader, &valueRaw); - // if a "=" or "<" character was hit, it means we read too far - size_t position = valueRaw.find_first_of("=<"); - if (position != valueRaw.npos) { - char hitChar = valueRaw[position]; + size_t endPosition = 0; - // if it was "=", rewind before the opcode name and spaces preceding - if (hitChar == '=') { - while (position > 0 && isRawOpcodeNameChar(valueRaw[position - 1])) - --position; - while (position > 0 && isSpaceChar(valueRaw[position - 1])) - --position; + for (size_t valueSize = valueRaw.size(); endPosition < valueSize;) { + size_t i = endPosition + 1; + + if (isSpaceChar(valueRaw[endPosition])) { + // check if the rest of the string is to consume or not + bool stop = false; + + // consume space characters following + while (i < valueSize && isSpaceChar(valueRaw[endPosition + 1])) + ++i; + + // if there aren't non-space characters following, do not extract + if (i == valueSize) + stop = true; + // if a "=" or "<" character is next, a header or a directive follows + else if (valueRaw[i] == '<' || valueRaw[i] == '#') + stop = true; + // if sequence of identifier chars and then "=", an opcode follows + else if (isIdentifierChar(valueRaw[i])) { + ++i; + while (i < valueSize && isIdentifierChar(valueRaw[i])) + ++i; + if (i < valueSize && valueRaw[i] == '=') + stop = true; + } + + if (stop) + break; } - absl::string_view excess(&valueRaw[position], valueRaw.size() - position); + endPosition = i; + } + + if (endPosition != valueRaw.size()) { + absl::string_view excess(&valueRaw[endPosition], valueRaw.size() - endPosition); reader.putBackChars(excess); - valueRaw.resize(position); - - // ensure that we are landing back next to a space char - if (hitChar == '=' && !reader.hasOneOfChars(" \t\r\n")) { - SourceLocation end = reader.location(); - emitError({ valueStart, end }, "Unexpected `=` in opcode value."); - recover(); - return; - } + valueRaw.resize(endPosition); } - while (!valueRaw.empty() && isSpaceChar(valueRaw.back())) { - reader.putBackChar(valueRaw.back()); - valueRaw.pop_back(); - } SourceLocation valueEnd = reader.location(); if (!_currentHeader) diff --git a/tests/ParsingT.cpp b/tests/ParsingT.cpp index 3249ac31..b54b40db 100644 --- a/tests/ParsingT.cpp +++ b/tests/ParsingT.cpp @@ -629,10 +629,43 @@ TEST_CASE("[Parsing] Opcode value special character") parser.setListener(&mock); parser.parseString("/opcodeValueSpecialCharacter.sfz", R"( -sample=Alto-Flute-sus-C#4-PB-loop.wav)"); +sample=Alto-Flute-sus-C#4-PB-loop.wav + +sample=foo=bar> expectedMembers = { {{"sample", "Alto-Flute-sus-C#4-PB-loop.wav"}}, + {{"sample", "foo=bar expectedHeaders = { + "region", "region" + }; + std::vector 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); +} + +TEST_CASE("[Parsing] Opcode value with inline directives") +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString("/opcodeValueWithInlineDirective.sfz", +R"(#define $VEL v1 sample=$VEL.wav #define $FOO bar)"); + + std::vector> expectedMembers = { + {{"sample", "v1.wav"}}, }; std::vector expectedHeaders = { "region"