SourceGenerator.h 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  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, const MappingType& 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 fork() { return SourceGenerator { m_builder, m_mapping, m_opening, m_closing }; }
  30. void set(StringView key, String value) { m_mapping.set(key, value); }
  31. String get(StringView key) const { return m_mapping.get(key).value(); }
  32. StringView as_string_view() const { return m_builder.string_view(); }
  33. String as_string() const { return m_builder.build(); }
  34. void append(StringView pattern)
  35. {
  36. GenericLexer lexer { pattern };
  37. while (!lexer.is_eof()) {
  38. // FIXME: It is a bit inconvenient, that 'consume_until' also consumes the 'stop' character, this makes
  39. // the method less generic because there is no way to check if the 'stop' character ever appeared.
  40. const auto consume_until_without_consuming_stop_character = [&](char stop) {
  41. return lexer.consume_while([&](char ch) { return ch != stop; });
  42. };
  43. m_builder.append(consume_until_without_consuming_stop_character(m_opening));
  44. if (lexer.consume_specific(m_opening)) {
  45. const auto placeholder = consume_until_without_consuming_stop_character(m_closing);
  46. if (!lexer.consume_specific(m_closing))
  47. VERIFY_NOT_REACHED();
  48. m_builder.append(get(placeholder));
  49. } else {
  50. VERIFY(lexer.is_eof());
  51. }
  52. }
  53. }
  54. private:
  55. StringBuilder& m_builder;
  56. MappingType m_mapping;
  57. char m_opening, m_closing;
  58. };
  59. }
  60. using AK::SourceGenerator;