SourceGenerator.h 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/GenericLexer.h>
  8. #include <AK/HashMap.h>
  9. #include <AK/String.h>
  10. #include <AK/StringBuilder.h>
  11. namespace AK {
  12. class SourceGenerator {
  13. AK_MAKE_NONCOPYABLE(SourceGenerator);
  14. public:
  15. using MappingType = HashMap<StringView, String>;
  16. explicit SourceGenerator(StringBuilder& builder, char opening = '@', char closing = '@')
  17. : m_builder(builder)
  18. , m_opening(opening)
  19. , m_closing(closing)
  20. {
  21. }
  22. explicit SourceGenerator(StringBuilder& builder, MappingType const& mapping, char opening = '@', char closing = '@')
  23. : m_builder(builder)
  24. , m_mapping(mapping)
  25. , m_opening(opening)
  26. , m_closing(closing)
  27. {
  28. }
  29. SourceGenerator(SourceGenerator&&) = default;
  30. SourceGenerator fork() { return SourceGenerator { m_builder, m_mapping, m_opening, m_closing }; }
  31. void set(StringView key, String value) { m_mapping.set(key, move(value)); }
  32. String get(StringView key) const
  33. {
  34. auto result = m_mapping.get(key);
  35. if (!result.has_value()) {
  36. warnln("No key named `{}` set on SourceGenerator", key);
  37. VERIFY_NOT_REACHED();
  38. }
  39. return result.release_value();
  40. }
  41. StringView as_string_view() const { return m_builder.string_view(); }
  42. String as_string() const { return m_builder.build(); }
  43. void append(StringView pattern)
  44. {
  45. GenericLexer lexer { pattern };
  46. while (!lexer.is_eof()) {
  47. // FIXME: It is a bit inconvenient, that 'consume_until' also consumes the 'stop' character, this makes
  48. // the method less generic because there is no way to check if the 'stop' character ever appeared.
  49. auto const consume_until_without_consuming_stop_character = [&](char stop) {
  50. return lexer.consume_while([&](char ch) { return ch != stop; });
  51. };
  52. m_builder.append(consume_until_without_consuming_stop_character(m_opening));
  53. if (lexer.consume_specific(m_opening)) {
  54. auto const placeholder = consume_until_without_consuming_stop_character(m_closing);
  55. if (!lexer.consume_specific(m_closing))
  56. VERIFY_NOT_REACHED();
  57. m_builder.append(get(placeholder));
  58. } else {
  59. VERIFY(lexer.is_eof());
  60. }
  61. }
  62. }
  63. void appendln(StringView pattern)
  64. {
  65. append(pattern);
  66. m_builder.append('\n');
  67. }
  68. template<size_t N>
  69. String get(char const (&key)[N])
  70. {
  71. return get(StringView { key, N - 1 });
  72. }
  73. template<size_t N>
  74. void set(char const (&key)[N], String value)
  75. {
  76. set(StringView { key, N - 1 }, value);
  77. }
  78. template<size_t N>
  79. void append(char const (&pattern)[N])
  80. {
  81. append(StringView { pattern, N - 1 });
  82. }
  83. template<size_t N>
  84. void appendln(char const (&pattern)[N])
  85. {
  86. appendln(StringView { pattern, N - 1 });
  87. }
  88. private:
  89. StringBuilder& m_builder;
  90. MappingType m_mapping;
  91. char m_opening, m_closing;
  92. };
  93. }
  94. using AK::SourceGenerator;