[YAML] Support extended spellings when parsing bools.

Support all the spellings of boolean datatypes according to https://yaml.org/type/bool.html

Reviewed By: silvas

Differential Revision: https://reviews.llvm.org/D92755
This commit is contained in:
Nathan James 2020-12-12 12:50:33 +00:00
parent f6e885ad2a
commit 0e5bfffb13
No known key found for this signature in database
GPG Key ID: CC007AFCDA90AA5F
5 changed files with 132 additions and 5 deletions

View File

@ -78,6 +78,9 @@ bool scanTokens(StringRef Input);
/// escaped, but emitted verbatim.
std::string escape(StringRef Input, bool EscapePrintable = true);
/// Parse \p S as a bool according to https://yaml.org/type/bool.html.
llvm::Optional<bool> parseBool(StringRef S);
/// This class represents a YAML stream potentially containing multiple
/// documents.
class Stream {

View File

@ -638,6 +638,7 @@ inline bool isNull(StringRef S) {
}
inline bool isBool(StringRef S) {
// FIXME: using parseBool is causing multiple tests to fail.
return S.equals("true") || S.equals("True") || S.equals("TRUE") ||
S.equals("false") || S.equals("False") || S.equals("FALSE");
}

View File

@ -746,6 +746,92 @@ std::string yaml::escape(StringRef Input, bool EscapePrintable) {
return EscapedInput;
}
llvm::Optional<bool> yaml::parseBool(StringRef S) {
switch (S.size()) {
case 1:
switch (S.front()) {
case 'y':
case 'Y':
return true;
case 'n':
case 'N':
return false;
default:
return None;
}
case 2:
switch (S.front()) {
case 'O':
if (S[1] == 'N') // ON
return true;
LLVM_FALLTHROUGH;
case 'o':
if (S[1] == 'n') //[Oo]n
return true;
return None;
case 'N':
if (S[1] == 'O') // NO
return false;
LLVM_FALLTHROUGH;
case 'n':
if (S[1] == 'o') //[Nn]o
return false;
return None;
default:
return None;
}
case 3:
switch (S.front()) {
case 'O':
if (S.drop_front() == "FF") // OFF
return false;
LLVM_FALLTHROUGH;
case 'o':
if (S.drop_front() == "ff") //[Oo]ff
return false;
return None;
case 'Y':
if (S.drop_front() == "ES") // YES
return true;
LLVM_FALLTHROUGH;
case 'y':
if (S.drop_front() == "es") //[Yy]es
return true;
return None;
default:
return None;
}
case 4:
switch (S.front()) {
case 'T':
if (S.drop_front() == "RUE") // TRUE
return true;
LLVM_FALLTHROUGH;
case 't':
if (S.drop_front() == "rue") //[Tt]rue
return true;
return None;
default:
return None;
}
case 5:
switch (S.front()) {
case 'F':
if (S.drop_front() == "ALSE") // FALSE
return false;
LLVM_FALLTHROUGH;
case 'f':
if (S.drop_front() == "alse") //[Ff]alse
return false;
return None;
default:
return None;
}
default:
return None;
}
}
Scanner::Scanner(StringRef Input, SourceMgr &sm, bool ShowColors,
std::error_code *EC)
: SM(sm), ShowColors(ShowColors), EC(EC) {

View File

@ -885,11 +885,8 @@ void ScalarTraits<bool>::output(const bool &Val, void *, raw_ostream &Out) {
}
StringRef ScalarTraits<bool>::input(StringRef Scalar, void *, bool &Val) {
if (Scalar.equals("true")) {
Val = true;
return StringRef();
} else if (Scalar.equals("false")) {
Val = false;
if (llvm::Optional<bool> Parsed = parseBool(Scalar)) {
Val = *Parsed;
return StringRef();
}
return "invalid boolean";

View File

@ -342,4 +342,44 @@ TEST(YAMLParser, FlowSequenceTokensOutsideFlowSequence) {
}
}
static void expectCanParseBool(StringRef S, bool Expected) {
llvm::Optional<bool> Parsed = yaml::parseBool(S);
EXPECT_TRUE(Parsed.hasValue());
EXPECT_EQ(*Parsed, Expected);
}
static void expectCannotParseBool(StringRef S) {
EXPECT_FALSE(yaml::parseBool(S).hasValue());
}
TEST(YAMLParser, ParsesBools) {
// Test true values.
expectCanParseBool("ON", true);
expectCanParseBool("On", true);
expectCanParseBool("on", true);
expectCanParseBool("TRUE", true);
expectCanParseBool("True", true);
expectCanParseBool("true", true);
expectCanParseBool("Y", true);
expectCanParseBool("y", true);
expectCanParseBool("YES", true);
expectCanParseBool("Yes", true);
expectCanParseBool("yes", true);
expectCannotParseBool("1");
// Test false values.
expectCanParseBool("FALSE", false);
expectCanParseBool("False", false);
expectCanParseBool("false", false);
expectCanParseBool("N", false);
expectCanParseBool("n", false);
expectCanParseBool("NO", false);
expectCanParseBool("No", false);
expectCanParseBool("no", false);
expectCanParseBool("OFF", false);
expectCanParseBool("Off", false);
expectCanParseBool("off", false);
expectCannotParseBool("0");
}
} // end namespace llvm