/src/serenity/AK/SourceGenerator.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, the SerenityOS developers. |
3 | | * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org> |
4 | | * Copyright (c) 2024, Tim Ledbetter <timledbetter@gmail.com> |
5 | | * |
6 | | * SPDX-License-Identifier: BSD-2-Clause |
7 | | */ |
8 | | |
9 | | #pragma once |
10 | | |
11 | | #include <AK/ByteString.h> |
12 | | #include <AK/GenericLexer.h> |
13 | | #include <AK/HashMap.h> |
14 | | #include <AK/String.h> |
15 | | #include <AK/StringBuilder.h> |
16 | | |
17 | | namespace AK { |
18 | | |
19 | | class SourceGenerator { |
20 | | AK_MAKE_NONCOPYABLE(SourceGenerator); |
21 | | |
22 | | public: |
23 | | using MappingType = HashMap<StringView, String>; |
24 | | |
25 | | explicit SourceGenerator(StringBuilder& builder, char opening = '@', char closing = '@', char escape = '\\') |
26 | 0 | : m_builder(builder) |
27 | 0 | , m_opening(opening) |
28 | 0 | , m_closing(closing) |
29 | 0 | , m_escape(escape) |
30 | 0 | { |
31 | 0 | } |
32 | | explicit SourceGenerator(StringBuilder& builder, MappingType&& mapping, char opening = '@', char closing = '@', char escape = '\\') |
33 | | : m_builder(builder) |
34 | | , m_mapping(move(mapping)) |
35 | | , m_opening(opening) |
36 | | , m_closing(closing) |
37 | | , m_escape(escape) |
38 | 0 | { |
39 | 0 | } |
40 | | |
41 | | SourceGenerator(SourceGenerator&&) = default; |
42 | | // Move-assign is undefinable due to 'StringBuilder& m_builder;' |
43 | | SourceGenerator& operator=(SourceGenerator&&) = delete; |
44 | | |
45 | | [[nodiscard]] SourceGenerator fork() |
46 | 0 | { |
47 | 0 | return SourceGenerator { m_builder, MUST(m_mapping.clone()), m_opening, m_closing }; |
48 | 0 | } |
49 | | |
50 | | void set(StringView key, String value) |
51 | 0 | { |
52 | 0 | if (key.contains(m_opening) || key.contains(m_closing)) { |
53 | 0 | warnln("SourceGenerator keys cannot contain the opening/closing delimiters `{}` and `{}`. (Keys are only wrapped in these when using them, not when setting them.)", m_opening, m_closing); |
54 | 0 | VERIFY_NOT_REACHED(); |
55 | 0 | } |
56 | 0 | m_mapping.set(key, move(value)); |
57 | 0 | } |
58 | | |
59 | | String get(StringView key) const |
60 | 0 | { |
61 | 0 | auto result = m_mapping.get(key); |
62 | 0 | if (!result.has_value()) { |
63 | 0 | warnln("No key named `{}` set on SourceGenerator", key); |
64 | 0 | VERIFY_NOT_REACHED(); |
65 | 0 | } |
66 | 0 | return result.release_value(); |
67 | 0 | } |
68 | | |
69 | 0 | StringView as_string_view() const { return m_builder.string_view(); } |
70 | | |
71 | | void append(StringView pattern) |
72 | 0 | { |
73 | 0 | GenericLexer lexer { pattern }; |
74 | |
|
75 | 0 | while (!lexer.is_eof()) { |
76 | 0 | m_builder.append(lexer.consume_until([&](char ch) { return ch == m_opening || ch == m_escape; })); |
77 | 0 | if (lexer.consume_specific(m_escape)) { |
78 | 0 | if (!(lexer.next_is(m_opening) || lexer.next_is(m_escape))) { |
79 | 0 | if (lexer.is_eof()) |
80 | 0 | warnln("Unexpected EOF while parsing escape sequence on SourceGenerator"); |
81 | 0 | else |
82 | 0 | warnln("Invalid escape sequence found `{}{}` on SourceGenerator", m_escape, lexer.peek()); |
83 | |
|
84 | 0 | VERIFY_NOT_REACHED(); |
85 | 0 | } |
86 | | |
87 | 0 | m_builder.append(lexer.consume()); |
88 | 0 | continue; |
89 | 0 | } |
90 | | |
91 | 0 | if (lexer.consume_specific(m_opening)) { |
92 | 0 | auto const placeholder = lexer.consume_until(m_closing); |
93 | |
|
94 | 0 | if (!lexer.consume_specific(m_closing)) |
95 | 0 | VERIFY_NOT_REACHED(); |
96 | | |
97 | 0 | m_builder.append(get(placeholder)); |
98 | 0 | } else { |
99 | 0 | VERIFY(lexer.is_eof()); |
100 | 0 | } |
101 | 0 | } |
102 | 0 | } |
103 | | |
104 | | void appendln(StringView pattern) |
105 | 0 | { |
106 | 0 | append(pattern); |
107 | 0 | m_builder.append('\n'); |
108 | 0 | } |
109 | | |
110 | | template<size_t N> |
111 | | String get(char const (&key)[N]) |
112 | | { |
113 | | return get(StringView { key, N - 1 }); |
114 | | } |
115 | | |
116 | | template<size_t N> |
117 | | void set(char const (&key)[N], String value) |
118 | 0 | { |
119 | 0 | set(StringView { key, N - 1 }, value); |
120 | 0 | } Unexecuted instantiation: void AK::SourceGenerator::set<11ul>(char const (&) [11ul], AK::String) Unexecuted instantiation: void AK::SourceGenerator::set<13ul>(char const (&) [13ul], AK::String) Unexecuted instantiation: void AK::SourceGenerator::set<16ul>(char const (&) [16ul], AK::String) |
121 | | |
122 | | template<size_t N> |
123 | | void append(char const (&pattern)[N]) |
124 | | { |
125 | | append(StringView { pattern, N - 1 }); |
126 | | } |
127 | | |
128 | | template<size_t N> |
129 | | void appendln(char const (&pattern)[N]) |
130 | | { |
131 | | appendln(StringView { pattern, N - 1 }); |
132 | | } |
133 | | |
134 | | // FIXME: These are deprecated. |
135 | | void set(StringView key, ByteString value) |
136 | 0 | { |
137 | 0 | set(key, MUST(String::from_byte_string(value))); |
138 | 0 | } |
139 | | template<size_t N> |
140 | | void set(char const (&key)[N], ByteString value) |
141 | 0 | { |
142 | 0 | set(StringView { key, N - 1 }, value); |
143 | 0 | } Unexecuted instantiation: void AK::SourceGenerator::set<11ul>(char const (&) [11ul], AK::ByteString) Unexecuted instantiation: void AK::SourceGenerator::set<14ul>(char const (&) [14ul], AK::ByteString) Unexecuted instantiation: void AK::SourceGenerator::set<5ul>(char const (&) [5ul], AK::ByteString) Unexecuted instantiation: void AK::SourceGenerator::set<9ul>(char const (&) [9ul], AK::ByteString) Unexecuted instantiation: void AK::SourceGenerator::set<13ul>(char const (&) [13ul], AK::ByteString) Unexecuted instantiation: void AK::SourceGenerator::set<16ul>(char const (&) [16ul], AK::ByteString) Unexecuted instantiation: void AK::SourceGenerator::set<10ul>(char const (&) [10ul], AK::ByteString) Unexecuted instantiation: void AK::SourceGenerator::set<8ul>(char const (&) [8ul], AK::ByteString) |
144 | | |
145 | | private: |
146 | | StringBuilder& m_builder; |
147 | | MappingType m_mapping; |
148 | | char m_opening { '@' }; |
149 | | char m_closing { '@' }; |
150 | | char m_escape { '\\' }; |
151 | | }; |
152 | | |
153 | | } |
154 | | |
155 | | #if USING_AK_GLOBALLY |
156 | | using AK::SourceGenerator; |
157 | | #endif |