format_analyser-inl.h 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #ifndef FORMAT_ANALYSER_INL_H_
  2. #error "Direct inclusion of this file is not allowed, include format_analyser.h"
  3. // For the sake of sane code completion.
  4. #include "format_analyser.h"
  5. #endif
  6. #include "format_arg.h"
  7. namespace NYT::NDetail {
  8. ////////////////////////////////////////////////////////////////////////////////
  9. consteval bool Contains(std::string_view sv, char symbol)
  10. {
  11. return sv.find(symbol) != std::string_view::npos;
  12. }
  13. template <class... TArgs>
  14. consteval void TFormatAnalyser::ValidateFormat(std::string_view format)
  15. {
  16. std::array<std::string_view, sizeof...(TArgs)> markers = {};
  17. std::array<TSpecifiers, sizeof...(TArgs)> specifiers{GetSpecifiers<TArgs>()...};
  18. int markerCount = 0;
  19. int currentMarkerStart = -1;
  20. for (int index = 0; index < std::ssize(format); ++index) {
  21. auto symbol = format[index];
  22. // Parse verbatim text.
  23. if (currentMarkerStart == -1) {
  24. if (symbol == IntroductorySymbol) {
  25. // Marker maybe begins.
  26. currentMarkerStart = index;
  27. }
  28. continue;
  29. }
  30. // NB: We check for %% first since
  31. // in order to verify if symbol is a specifier
  32. // we need markerCount to be within range of our
  33. // specifier array.
  34. if (symbol == IntroductorySymbol) {
  35. if (currentMarkerStart + 1 != index) {
  36. // '%a% detected'
  37. CrashCompilerWrongTermination("You may not terminate flag sequence other than %% with \'%\' symbol");
  38. return;
  39. }
  40. // '%%' detected --- skip
  41. currentMarkerStart = -1;
  42. continue;
  43. }
  44. // We are inside of marker.
  45. if (markerCount == std::ssize(markers)) {
  46. // To many markers
  47. CrashCompilerNotEnoughArguments("Number of arguments supplied to format is smaller than the number of flag sequences");
  48. return;
  49. }
  50. if (Contains(specifiers[markerCount].Conversion, symbol)) {
  51. // Marker has finished.
  52. markers[markerCount]
  53. = std::string_view(format.begin() + currentMarkerStart, index - currentMarkerStart + 1);
  54. currentMarkerStart = -1;
  55. ++markerCount;
  56. continue;
  57. }
  58. if (!Contains(specifiers[markerCount].Flags, symbol)) {
  59. CrashCompilerWrongFlagSpecifier("Symbol is not a valid flag specifier; See FlagSpecifiers");
  60. }
  61. }
  62. if (currentMarkerStart != -1) {
  63. // Runaway marker.
  64. CrashCompilerMissingTermination("Unterminated flag sequence detected; Use \'%%\' to type plain %");
  65. return;
  66. }
  67. if (markerCount < std::ssize(markers)) {
  68. // Missing markers.
  69. CrashCompilerTooManyArguments("Number of arguments supplied to format is greater than the number of flag sequences");
  70. return;
  71. }
  72. // TODO(arkady-e1ppa): Consider per-type verification
  73. // of markers.
  74. }
  75. template <class TArg>
  76. consteval auto TFormatAnalyser::GetSpecifiers()
  77. {
  78. if constexpr (!CFormattable<TArg>) {
  79. CrashCompilerNotFormattable<TArg>("Your specialization of TFormatArg is broken");
  80. }
  81. return TSpecifiers{
  82. .Conversion = std::string_view{
  83. std::data(TFormatArg<TArg>::ConversionSpecifiers),
  84. std::size(TFormatArg<TArg>::ConversionSpecifiers)},
  85. .Flags = std::string_view{
  86. std::data(TFormatArg<TArg>::FlagSpecifiers),
  87. std::size(TFormatArg<TArg>::FlagSpecifiers)},
  88. };
  89. }
  90. ////////////////////////////////////////////////////////////////////////////////
  91. } // namespace NYT::NDetail