SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
Text.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cctype>
5
6#include <BufferStream.h>
7
8using namespace sourcepp;
9
10namespace sourcepp::parser::text {
11
13 {'\'', '\''},
14 {'\"', '\"'},
15 {'?', '?'},
16 {'\\', '\\'},
17 {'a', '\a'},
18 {'b', '\b'},
19 {'f', '\f'},
20 {'n', '\n'},
21 {'r', '\r'},
22 {'t', '\t'},
23 {'v', '\v'},
24};
25
27
28} // namespace parser::text
29
31 return c == '\n' || c == '\r';
32}
33
34bool parser::text::isNewLine(std::string_view str) {
35 return std::all_of(str.begin(), str.end(), [](char c) { return isNewLine(c); });
36}
37
39 return c == ' ' || c == '\a' || c == '\f' || c == '\t' || c == '\v' || isNewLine(c);
40}
41
42bool parser::text::isWhitespace(std::string_view str) {
43 return std::all_of(str.begin(), str.end(), [](char c) { return isWhitespace(c); });
44}
45
47 return std::isdigit(c);
48}
49
50bool parser::text::isNumber(std::string_view str) {
51 return std::all_of(str.begin(), str.end(), [](char c) { return isNumber(c); });
52}
53
54std::string parser::text::convertSpecialCharsToEscapes(std::string_view str, const EscapeSequenceMap& escapeSequences) {
55 // Reverse escape sequences map (assume that it's bidirectional)
56 EscapeSequenceMap specialSequences;
57 for (const auto& [normal, special] : escapeSequences) {
58 specialSequences[special] = normal;
59 }
60
61 std::string out;
62 for (int i = 0; i < str.length(); i++) {
63 if (specialSequences.contains(str[i])) {
64 out += '\\';
65 out += specialSequences[str[i]];
66 } else {
67 out += str[i];
68 }
69 }
70 return out;
71}
72
73std::string parser::text::convertEscapesToSpecialChars(std::string_view str, const EscapeSequenceMap& escapeSequences) {
74 std::string out;
75 for (int i = 0; i < str.length(); i++) {
76 if (!escapeSequences.empty() && str[i] == '\\' && i < str.length() - 1) {
77 auto n = str[i + 1];
78 if (escapeSequences.contains(n)) {
79 out += escapeSequences.at(n);
80 } else {
81 out += str[i];
82 out += str[i + 1];
83 }
84 } else {
85 out += str[i];
86 }
87 }
88 return out;
89}
90
91void parser::text::eatWhitespace(BufferStream& stream) {
92 while (isWhitespace(stream.read<char>())) {}
93 stream.seek(-1, std::ios::cur);
94}
95
96void parser::text::eatSingleLineComment(BufferStream& stream) {
97 while (!isNewLine(stream.read<char>())) {}
98}
99
100void parser::text::eatMultiLineComment(BufferStream& stream, std::string_view multiLineCommentEnd) {
101 while (!std::ranges::equal(stream.read_span<char>(multiLineCommentEnd.length()), multiLineCommentEnd)) {
102 stream.seek(-static_cast<int64_t>(multiLineCommentEnd.length() - 1), std::ios::cur);
103 }
104}
105
106void parser::text::eatWhitespaceAndSingleLineComments(BufferStream& stream, std::string_view singleLineCommentStart) {
107 return eatWhitespaceAndComments(stream, singleLineCommentStart, "");
108}
109
110void parser::text::eatWhitespaceAndMultiLineComments(BufferStream& stream, std::string_view multiLineCommentStart) {
111 return eatWhitespaceAndComments(stream, "", multiLineCommentStart);
112}
113
114// NOLINTNEXTLINE(*-no-recursion)
115void parser::text::eatWhitespaceAndComments(BufferStream& stream, std::string_view singleLineCommentStart, std::string_view multiLineCommentStart) {
116 eatWhitespace(stream);
117
118 if (!singleLineCommentStart.empty()) {
119 if (std::ranges::equal(stream.read_span<char>(singleLineCommentStart.length()), singleLineCommentStart)) {
120 eatSingleLineComment(stream);
121 eatWhitespaceAndComments(stream, singleLineCommentStart, multiLineCommentStart);
122 return;
123 }
124 stream.seek(-static_cast<int64_t>(singleLineCommentStart.length()), std::ios::cur);
125 }
126
127 if (!multiLineCommentStart.empty()) {
128 if (std::ranges::equal(stream.read_span<char>(multiLineCommentStart.length()), multiLineCommentStart)) {
129 eatMultiLineComment(stream);
130 eatWhitespaceAndComments(stream, singleLineCommentStart, multiLineCommentStart);
131 return;
132 }
133 stream.seek(-static_cast<int64_t>(multiLineCommentStart.length()), std::ios::cur);
134 }
135}
136
137bool parser::text::tryToEatChar(BufferStream& stream, char c) {
138 if (stream.peek<char>() != c) {
139 return false;
140 }
141 stream.skip();
142 return true;
143}
144
145std::string_view parser::text::readStringToBuffer(BufferStream& stream, BufferStream& backing, std::string_view start, std::string_view end, const EscapeSequenceMap& escapeSequences) {
146 const auto startSpan = backing.tell();
147
148 bool stopAtWhitespace = true;
149 char c = stream.read<char>();
150 if (start.find(c) != std::string_view::npos) {
151 stopAtWhitespace = false;
152 } else {
153 backing << c;
154 }
155
156 for (c = stream.read<char>(); (stopAtWhitespace && !isWhitespace(c)) || (!stopAtWhitespace && end.find(c) == std::string_view::npos); c = stream.read<char>()) {
157 if (!escapeSequences.empty() && c == '\\') {
158 auto n = stream.read<char>();
159 if (stopAtWhitespace && isWhitespace(n)) {
160 break;
161 }
162 if (escapeSequences.contains(n)) {
163 backing << escapeSequences.at(n);
164 } else if (!stopAtWhitespace && end.find(n) != std::string_view::npos) {
165 break;
166 } else {
167 backing << c << n;
168 }
169 } else {
170 backing << c;
171 }
172 }
173
174 backing << '\0';
175 return {reinterpret_cast<const char*>(backing.data()) + startSpan, backing.tell() - 1 - startSpan};
176}
177
178std::string_view parser::text::readUnquotedStringToBuffer(BufferStream& stream, BufferStream& backing, const EscapeSequenceMap& escapeSequences) {
179 return readStringToBuffer(stream, backing, "", "", escapeSequences);
180}
181
182std::string_view parser::text::readUnquotedStringToBuffer(BufferStream& stream, BufferStream& backing, std::string_view end, const EscapeSequenceMap& escapeSequences) {
183 const auto startSpan = backing.tell();
184
185 for (char c = stream.read<char>(); !isWhitespace(c) && end.find(c) == std::string_view::npos; c = stream.read<char>()) {
186 if (!escapeSequences.empty() && c == '\\') {
187 auto n = stream.read<char>();
188 if (escapeSequences.contains(n)) {
189 backing << escapeSequences.at(n);
190 } else if (isWhitespace(n) || end.find(n) != std::string_view::npos) {
191 break;
192 } else {
193 backing << c << n;
194 }
195 } else {
196 backing << c;
197 }
198 }
199
200 backing << '\0';
201 return {reinterpret_cast<const char*>(backing.data()) + startSpan, backing.tell() - 1 - startSpan};
202}
std::string_view readStringToBuffer(BufferStream &stream, BufferStream &backing, std::string_view start=DEFAULT_STRING_START, std::string_view end=DEFAULT_STRING_END, const EscapeSequenceMap &escapeSequences=DEFAULT_ESCAPE_SEQUENCES)
Read a string starting at the current stream position.
Definition: Text.cpp:145
std::string_view readUnquotedStringToBuffer(BufferStream &stream, BufferStream &backing, const EscapeSequenceMap &escapeSequences=DEFAULT_ESCAPE_SEQUENCES)
Read a string starting at the current stream position.
Definition: Text.cpp:178
std::string convertSpecialCharsToEscapes(std::string_view str, const EscapeSequenceMap &escapeSequences)
Convert special characters like \n to escaped special characters like \\n.
Definition: Text.cpp:54
std::string convertEscapesToSpecialChars(std::string_view str, const EscapeSequenceMap &escapeSequences)
Convert escaped special characters like \\n to special characters like \n.
Definition: Text.cpp:73
const EscapeSequenceMap NO_ESCAPE_SEQUENCES
Definition: Text.cpp:26
std::unordered_map< char, char > EscapeSequenceMap
Definition: Text.h:17
void eatMultiLineComment(BufferStream &stream, std::string_view multiLineCommentEnd=DEFAULT_MULTI_LINE_COMMENT_END)
If a multi line comment is detected, eat its contents.
Definition: Text.cpp:100
bool isNewLine(char c)
If a char is a newline character.
Definition: Text.cpp:30
void eatSingleLineComment(BufferStream &stream)
If a single line comment is detected, eat its contents.
Definition: Text.cpp:96
void eatWhitespaceAndSingleLineComments(BufferStream &stream, std::string_view singleLineCommentStart=DEFAULT_SINGLE_LINE_COMMENT_START)
Eat all whitespace and single line comments after the current stream position.
Definition: Text.cpp:106
void eatWhitespace(BufferStream &stream)
Eat all whitespace after the current stream position.
Definition: Text.cpp:91
const EscapeSequenceMap DEFAULT_ESCAPE_SEQUENCES
Definition: Text.cpp:12
bool tryToEatChar(BufferStream &stream, char c)
If the given char exists at the current position, skip over it.
Definition: Text.cpp:137
void eatWhitespaceAndComments(BufferStream &stream, std::string_view singleLineCommentStart=DEFAULT_SINGLE_LINE_COMMENT_START, std::string_view multiLineCommentStart=DEFAULT_MULTI_LINE_COMMENT_START)
Eat all whitespace and comments after the current stream position.
Definition: Text.cpp:115
bool isNumber(char c)
If a char is a numerical character (0-9).
Definition: Text.cpp:46
void eatWhitespaceAndMultiLineComments(BufferStream &stream, std::string_view multiLineCommentStart=DEFAULT_MULTI_LINE_COMMENT_START)
Eat all whitespace and multi line comments after the current stream position.
Definition: Text.cpp:110
bool isWhitespace(char c)
If a char is a whitespace character.
Definition: Text.cpp:38
Definition: LZMA.h:11