diff --git a/src/language/PEGGrammar.hpp b/src/language/PEGGrammar.hpp
index fb3b1d74abf6ba0d33113ec95996c6a270edb77d..553a2797c8cf227ab88d4b977f9f727b671a4c7a 100644
--- a/src/language/PEGGrammar.hpp
+++ b/src/language/PEGGrammar.hpp
@@ -55,11 +55,13 @@ struct character : if_must_else< one< '\\' >, escaped_c, ascii::any> {};
 struct open_parent : seq< one< '(' >, ignored > {};
 struct close_parent : seq< one< ')' >, ignored > {};
 
-struct literal : if_must< one< '"' >, until< one< '"' >, character > > {};
+struct literal : star< minus<character, one < '"' > > >{};
+
+struct quoted_literal : if_must< one< '"' >, seq< literal,  one< '"'  > > >{};
 
 struct import_kw :  TAO_PEGTL_KEYWORD("import") {};
 
-struct LITERAL : seq< literal, ignored >{};
+struct LITERAL : seq< quoted_literal, ignored >{};
 
 struct REAL : seq< real, ignored >{};
 
diff --git a/src/language/utils/ASTPrinter.cpp b/src/language/utils/ASTPrinter.cpp
index 9bf18bdcf88f6399283750dac9ab0b38d8bf0400..8cc88b530997eb3cc9cccb176c3812c41dceea1f 100644
--- a/src/language/utils/ASTPrinter.cpp
+++ b/src/language/utils/ASTPrinter.cpp
@@ -14,9 +14,10 @@ ASTPrinter::_print(std::ostream& os, const ASTNode& node) const
   }
   os << rang::fg::reset;
 
-  if (node.is_type<language::name>() or node.is_type<language::literal>() or node.is_type<language::integer>() or
-      node.is_type<language::real>()) {
+  if (node.is_type<language::name>() or node.is_type<language::integer>() or node.is_type<language::real>()) {
     os << ':' << rang::fgB::green << node.string() << rang::fg::reset;
+  } else if (node.is_type<language::literal>()) {
+    os << ":\"" << rang::fgB::green << node.string() << rang::fg::reset << '"';
   }
 
   if (m_info & static_cast<InfoBaseType>(Info::data_type)) {
diff --git a/src/utils/EscapedString.hpp b/src/utils/EscapedString.hpp
index ed525abd0368614fb756fd8c7795bb1c7ea178c6..cf0f0c676ebd2998673c62870b07e52bf04e0269 100644
--- a/src/utils/EscapedString.hpp
+++ b/src/utils/EscapedString.hpp
@@ -1,6 +1,7 @@
 #ifndef ESCAPED_STRING_HPP
 #define ESCAPED_STRING_HPP
 
+#include <utils/Exceptions.hpp>
 #include <utils/PugsMacros.hpp>
 
 #include <sstream>
@@ -11,7 +12,7 @@ PUGS_INLINE std::string
 unescapeString(std::string_view input_string)
 {
   std::stringstream ss;
-  for (size_t i = 1; i < input_string.size() - 1; ++i) {
+  for (size_t i = 0; i < input_string.size(); ++i) {
     char c = input_string[i];
     if (c == '\\') {
       ++i;
@@ -81,11 +82,15 @@ escapeString(std::string_view input_string)
       ss << R"(\\)";
       break;
     }
+    case '\'': {
+      ss << R"(\')";
+      break;
+    }
     case '\"': {
       ss << R"(\")";
       break;
     }
-    case '?': {
+    case '\?': {
       ss << R"(\?)";
       break;
     }
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 027b1293fd477360d3dffc78da4ab1f2417ee3a6..b58ed3cf808860f9500755dd1cebecb1e255d05f 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -60,6 +60,7 @@ add_executable (unit_tests
   test_Demangle.cpp
   test_DoWhileProcessor.cpp
   test_EmbeddedData.cpp
+  test_EscapedString.cpp
   test_ExecutionPolicy.cpp
   test_FakeProcessor.cpp
   test_ForProcessor.cpp
diff --git a/tests/test_EscapedString.cpp b/tests/test_EscapedString.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..925ebadbda1f8851d7d2ee208bbaa619b8dd435b
--- /dev/null
+++ b/tests/test_EscapedString.cpp
@@ -0,0 +1,27 @@
+#ifndef TEST_ESCAPED_STRING_HPP
+#define TEST_ESCAPED_STRING_HPP
+
+#include <catch2/catch.hpp>
+
+#include <utils/EscapedString.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("EscapedString", "[utils]")
+{
+  SECTION("escape string")
+  {
+    const std::string s = "foo\'\\\"\?\a\b\f\n\r\t\vbar";
+
+    REQUIRE(escapeString(s) == R"(foo\'\\\"\?\a\b\f\n\r\t\vbar)");
+  }
+
+  SECTION("unescape string")
+  {
+    const std::string s = R"(foo\'\\\"\?\a\b\f\n\r\t\vbar)";
+
+    REQUIRE(unescapeString(s) == std::string{"foo\'\\\"\?\a\b\f\n\r\t\vbar"});
+  }
+}
+
+#endif   // TEST_ESCAPED_STRING_HPP