Skip to content

Commit

Permalink
Do no longer escape backslashes when printing strings or bytes.
Browse files Browse the repository at this point in the history
Escaping backslashes in `print` output seems both unnecessary and
confusing.

Closes #1895.
  • Loading branch information
rsmmr committed Oct 23, 2024
1 parent b439d1d commit edf96cb
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 15 deletions.
4 changes: 2 additions & 2 deletions hilti/runtime/include/types/bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ class Bytes : protected std::string {
};

inline std::ostream& operator<<(std::ostream& out, const Bytes& x) {
out << escapeBytes(x.str());
out << escapeBytes(x.str(), render_style::Bytes::NoEscapeBackslash);
return out;
}

Expand All @@ -584,7 +584,7 @@ inline Bytes operator"" _b(const char* str, size_t size) { return Bytes(Bytes::B

template<>
inline std::string detail::to_string_for_print<Bytes>(const Bytes& x) {
return escapeBytes(x.str());
return escapeBytes(x.str(), render_style::Bytes::NoEscapeBackslash);
}

namespace detail::adl {
Expand Down
6 changes: 4 additions & 2 deletions hilti/runtime/include/types/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,14 @@ inline std::string to_string(const CharT (&x)[N], adl::tag /*unused*/) {

template<>
inline std::string detail::to_string_for_print<std::string>(const std::string& x) {
return escapeUTF8(x, render_style::UTF8::NoEscapeHex | render_style::UTF8::NoEscapeControl);
return escapeUTF8(x, render_style::UTF8::NoEscapeHex | render_style::UTF8::NoEscapeControl |
render_style::UTF8::NoEscapeBackslash);
}

template<>
inline std::string detail::to_string_for_print<std::string_view>(const std::string_view& x) {
return escapeUTF8(x, render_style::UTF8::NoEscapeHex | render_style::UTF8::NoEscapeControl);
return escapeUTF8(x, render_style::UTF8::NoEscapeHex | render_style::UTF8::NoEscapeControl |
render_style::UTF8::NoEscapeBackslash);
}

// Specialization for string literals. Since `to_string_for_print` is not
Expand Down
16 changes: 9 additions & 7 deletions hilti/runtime/include/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,10 @@ namespace render_style {
* the default style accordingly.
*/
enum class Bytes {
Default = 0, /**< name for unmodified default style */
EscapeQuotes = (1U << 1U), /**< escape double quotes with backslashes */
UseOctal = (1U << 2U), /**< escape non-printables with `\NNN` instead of `\xNN` */
Default = 0, /**< name for unmodified default style */
EscapeQuotes = (1U << 1U), /**< escape double quotes with backslashes */
UseOctal = (1U << 2U), /**< escape non-printables with `\NNN` instead of `\xNN` */
NoEscapeBackslash = (1U << 3U), /**< do not escape backslashes */
};

/**
Expand All @@ -311,11 +312,12 @@ enum class Bytes {
* through `expandUTF8Escapes()`.
*/
enum class UTF8 {
Default = 0, /**< name for unmodified default style */
EscapeQuotes = (1U << 1U), /**< escape double quotes with backslashes */
NoEscapeControl = (1U << 2U), /**< do not escape control characters and null bytes */
Default = 0, /**< name for unmodified default style */
EscapeQuotes = (1U << 1U), /**< escape double quotes with backslashes */
NoEscapeBackslash = (1U << 2U), /**< do not escape backslashes; this may leave the result non-reversible */
NoEscapeControl = (1U << 3U), /**< do not escape control characters and null bytes */
NoEscapeHex =
(1U << 3U), /**< do not escape already existing `\xNN` escape codes; this may leave the result non-reversible */
(1U << 4U), /**< do not escape already existing `\xNN` escape codes; this may leave the result non-reversible */
};

} // namespace render_style
Expand Down
9 changes: 9 additions & 0 deletions hilti/runtime/src/tests/bytes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -653,4 +653,13 @@ TEST_CASE("issue 599") {
CHECK_EQ(a->toInt(), 31);
}

TEST_CASE("to_string") {
CHECK_EQ(to_string("abc"_b), "b\"abc\"");
CHECK_EQ(to_string("\"\\"_b), "b\"\\\"\\\\\"");
}

TEST_CASE("to_string_for_print") {
CHECK_EQ(to_string_for_print("abc"_b), "abc");
CHECK_EQ(to_string_for_print("\\\""_b), "\\\"");
}
TEST_SUITE_END();
4 changes: 2 additions & 2 deletions hilti/runtime/src/tests/deferred-expression.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ TEST_CASE("to_string_for_print") {
auto expr = make_deferred<Bytes>([&i]() { return Bytes(fmt("\\x0%d", ++i)); });

// Stringification evaluates the expression.
CHECK_EQ(to_string_for_print(expr), R"#(\\x01)#");
CHECK_EQ(to_string_for_print(expr), R"#(\\x02)#");
CHECK_EQ(to_string_for_print(expr), R"#(\x01)#");
CHECK_EQ(to_string_for_print(expr), R"#(\x02)#");
}

TEST_SUITE_END();
4 changes: 4 additions & 0 deletions hilti/runtime/src/tests/string.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,16 @@ TEST_CASE("to_string") {
CHECK_EQ(to_string(std::string("abc")), "\"abc\"");
CHECK_EQ(to_string(std::string_view("abc")), "\"abc\"");
CHECK_EQ(to_string("abc"), "\"abc\"");
CHECK_EQ(to_string("\"\\"), "\"\\\"\\\\\"");
}

TEST_CASE("to_string_for_print") {
CHECK_EQ(to_string_for_print(std::string("abc")), "abc");
CHECK_EQ(to_string_for_print(std::string_view("abc")), "abc");
CHECK_EQ(to_string_for_print("abc"), "abc");
CHECK_EQ(to_string_for_print(std::string("\\\"")), "\\\"");
CHECK_EQ(to_string_for_print(std::string_view("\\\"")), "\\\"");
CHECK_EQ(to_string_for_print("\\\""), "\\\"");
}

TEST_CASE("split") {
Expand Down
6 changes: 4 additions & 2 deletions hilti/runtime/src/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,10 @@ std::string hilti::rt::escapeUTF8(std::string_view s, bitmask<render_style::UTF8
if ( cp == '\\' ) {
if ( (style & render_style::UTF8::NoEscapeHex) && (p + n) < e && *(p + n) == 'x' )
esc += "\\";
else
else if ( ! (style & render_style::UTF8::NoEscapeBackslash) )
esc += "\\\\";
else
esc += "\\";
}

else if ( cp == '"' && (style & render_style::UTF8::EscapeQuotes) )
Expand Down Expand Up @@ -346,7 +348,7 @@ std::string hilti::rt::escapeBytes(std::string_view s, bitmask<render_style::Byt
std::string esc;

while ( p < e ) {
if ( *p == '\\' )
if ( *p == '\\' && ! (style & render_style::Bytes::NoEscapeBackslash) )
esc += "\\\\";

else if ( *p == '"' && (style & render_style::Bytes::EscapeQuotes) )
Expand Down
4 changes: 4 additions & 0 deletions tests/Baseline/spicy.rt.print-escaping/output
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
\", \"
\", \"
5C22, 5C22
17 changes: 17 additions & 0 deletions tests/spicy/rt/print-escaping.spicy
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# @TEST-EXEC: spicyc -j -d %INPUT >output
# @TEST-EXEC: btest-diff output
#
# @TEST-DOC: Check that we escape certain special characters in strings correctly when printing them.

module test;

import spicy;

global x = b"\\\"";
global y = "\\\"";

print x, y;
print "%s, %s" % (x, y);

# for comparison
print spicy::bytes_to_hexstring(x), spicy::bytes_to_hexstring(y.encode());

0 comments on commit edf96cb

Please sign in to comment.