diff --git a/llvm/include/llvm/Support/YAMLParser.h b/llvm/include/llvm/Support/YAMLParser.h --- a/llvm/include/llvm/Support/YAMLParser.h +++ b/llvm/include/llvm/Support/YAMLParser.h @@ -78,6 +78,9 @@ /// 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 parseBool(StringRef S); + /// This class represents a YAML stream potentially containing multiple /// documents. class Stream { diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -638,6 +638,7 @@ } 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"); } diff --git a/llvm/lib/Support/YAMLParser.cpp b/llvm/lib/Support/YAMLParser.cpp --- a/llvm/lib/Support/YAMLParser.cpp +++ b/llvm/lib/Support/YAMLParser.cpp @@ -746,6 +746,92 @@ return EscapedInput; } +llvm::Optional 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) { diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp --- a/llvm/lib/Support/YAMLTraits.cpp +++ b/llvm/lib/Support/YAMLTraits.cpp @@ -885,11 +885,8 @@ } StringRef ScalarTraits::input(StringRef Scalar, void *, bool &Val) { - if (Scalar.equals("true")) { - Val = true; - return StringRef(); - } else if (Scalar.equals("false")) { - Val = false; + if (llvm::Optional Parsed = parseBool(Scalar)) { + Val = *Parsed; return StringRef(); } return "invalid boolean"; diff --git a/llvm/unittests/Support/YAMLParserTest.cpp b/llvm/unittests/Support/YAMLParserTest.cpp --- a/llvm/unittests/Support/YAMLParserTest.cpp +++ b/llvm/unittests/Support/YAMLParserTest.cpp @@ -342,4 +342,44 @@ } } +static void expectCanParseBool(StringRef S, bool Expected) { + llvm::Optional 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