Skip to content

Commit dce1b01

Browse files
committed
Prevent identifiers starting with digit
1 parent 5b3581c commit dce1b01

File tree

3 files changed

+43
-10
lines changed

3 files changed

+43
-10
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ Usage: res2cpp [-options]
77
-c, --config <file> sets the path of the config file (required).
88
-s, --source <file> sets the path of the source file.
99
-h, --header <file> sets the path of the header file.
10-
-n, --native reduce overhead by optimizing for native endianness.
1110
-d, --data <type> use type for data (e.g. uint8_t, std::byte, void)
1211
-t, --type <type> use type for resource (e.g. std::span<const uint8_t>).
1312
-a, --alias <type> declare an alias for resource type.
1413
-i, --include <file> add #include to generated header.
14+
-n, --native optimize for native endianness to improve compile-time.
1515
```
1616

1717
## Building

res2cpp.cpp

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ void print_help_message() {
1919
" -s, --source <file> sets the path of the source file.\n"
2020
" -h, --header <file> sets the path of the header file.\n"
2121
//" -e, --embed use #embed for embedding files.\n"
22-
" -n, --native reduce overhead by optimizing for native endianness.\n"
2322
" -d, --data <type> use type for data (e.g. uint8_t, std::byte, void)\n"
2423
" -t, --type <type> use type for resource (e.g. std::span<const uint8_t>).\n"
2524
" -a, --alias <type> declare an alias for resource type.\n"
2625
" -i, --include <file> add #include to generated header.\n"
26+
" -n, --native optimize for native endianness to improve compile-time.\n"
2727
"\n"
2828
"All Rights Reserved.\n"
2929
"This program comes with absolutely no warranty.\n"
@@ -69,7 +69,11 @@ bool is_space(char c) {
6969
}
7070

7171
bool is_alnum(char c) {
72-
return (std::isalnum(static_cast<unsigned char>(c)) || c == '_');
72+
return (std::isalnum(static_cast<unsigned char>(c)));
73+
}
74+
75+
bool is_digit(char c) {
76+
return (std::isdigit(static_cast<unsigned char>(c)));
7377
}
7478

7579
std::filesystem::path utf8_to_path(std::string_view utf8_string) {
@@ -207,21 +211,42 @@ std::string normalize_path(std::string&& path) {
207211
}
208212

209213
std::string normalize_id(std::string&& id) {
210-
id = replace_all(
214+
return replace_all(
211215
replace_all(std::move(id), "/", "$"), "::", "/");
212-
for (const auto& c : id)
213-
if (!is_alnum(c) && c != '/')
214-
error("invalid identifier");
215-
if (!id.empty() && id.back() == '/')
216-
error("invalid identifier");
217-
return id;
216+
}
217+
218+
bool is_valid_identifier(std::string_view id) {
219+
if (id.empty())
220+
return false;
221+
222+
auto after_slash = true;
223+
for (const auto& c : id) {
224+
if (!is_alnum(c) && c != '_' && c != '/')
225+
return false;
226+
if (after_slash && is_digit(c))
227+
return false;
228+
after_slash = (c == '/');
229+
}
230+
if (id.back() == '/')
231+
return false;
232+
return true;
218233
}
219234

220235
std::string deduce_id_from_path(bool is_header, const std::string& path) {
221236
auto id = trim(is_header ? path : remove_extension(path));
237+
238+
// replace not allowed characters
222239
for (auto& c : id)
223240
if (!is_alnum(c) && c != '/')
224241
c = '_';
242+
243+
// insert _ before initial digits
244+
auto after_slash = true;
245+
for (auto it = id.begin(); it != id.end(); ++it) {
246+
if (after_slash && is_digit(*it))
247+
it = id.insert(it, '_');
248+
after_slash = (*it == '/');
249+
}
225250
return id;
226251
}
227252

@@ -307,6 +332,9 @@ std::optional<Definition> parse_definition(const std::string& line) {
307332
else if (skip_until('=')) {
308333
// first is no string
309334
definition.id = normalize_id(trim({ begin, it }));
335+
if (!definition.id.empty() &&
336+
!is_valid_identifier(definition.id))
337+
error("invalid identifier");
310338
++it;
311339
skip_space();
312340
begin = it;

test.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ void test_parse_definition() {
5252
assert(check("a=b", "a", "b", false));
5353
assert(check(" a = b c # x", "a", "b c", false));
5454
assert(check(" a = ' b c [' # x", "a", " b c [", false));
55+
assert(check(" _1 = b", "_1", "b", false));
5556

5657
assert(check_throws("["));
5758
assert(check_throws("]"));
@@ -76,6 +77,8 @@ void test_parse_definition() {
7677
assert(check_throws(" a: = b "));
7778
assert(check_throws(" a:b = b "));
7879
assert(check_throws(" a:: = b "));
80+
assert(check_throws(" ? = b "));
81+
assert(check_throws(" 1 = b "));
7982

8083
// normalize path / deducing id from path
8184
assert(check("a/b.txt", "a/b", "a/b.txt", false));
@@ -89,6 +92,8 @@ void test_parse_definition() {
8992
assert(check("[a/b.txt]", "a/b_txt", "a/b.txt", true));
9093
assert(check("[ a\\b.txt]", "a/b_txt", "a/b.txt", true));
9194
assert(check("[a\\b.txt ]", "a/b_txt", "a/b.txt", true));
95+
assert(check("1/2.txt", "_1/_2", "1/2.txt", false));
96+
assert(check("[1/2]", "_1/_2", "1/2", true));
9297

9398
// normalize id
9499
assert(check("a::b = c", "a/b", "c", false));

0 commit comments

Comments
 (0)