Repository: mesonbuild/meson

Issue:
String literals cannot contain literal '\n'
At the moment, `\\` is replaced by `\` before handling of `\n`. This means string literals cannot be made with the sequence `\n` in them. For instance,
~~~Meson
message('c:\\fun\\name')
~~~
results in a string where there is a newline between 'fun' and 'ame'.

Perhaps something along the lines of:

~~~diff
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index 8400a1ac..5a91a3b9 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -111,6 +111,7 @@ class Lexer:
         par_count = 0
         bracket_count = 0
         col = 0
+        newline_rx = re.compile(r'(?<!\\)(\\\\)*\\n')
         while loc < len(self.code):
             matched = False
             value = None
@@ -139,10 +140,9 @@ class Lexer:
                     elif tid == 'dblquote':
                         raise ParseException('Double quotes are not supported. Use single quotes.', self.getline(line_start), lineno, col)
                     elif tid == 'string':
-                        value = match_text[1:-1]\
-                            .replace(r"\'", "'")\
-                            .replace(r" \\ ".strip(), r" \ ".strip())\
-                            .replace("\\n", "\n")
+                        value = match_text[1:-1].replace(r"\'", "'")
+                        value = newline_rx.sub(r'\1\n', value)
+                        value = value.replace(r" \\ ".strip(), r" \ ".strip())
                     elif tid == 'multiline_string':
                         tid = 'string'
                         value = match_text[3:-3]
~~~


Discussion:

Resolving patch:
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -111,6 +111,7 @@ def lex(self, subdir):
         par_count = 0
         bracket_count = 0
         col = 0
+        newline_rx = re.compile(r'(?<!\\)((?:\\\\)*)\\n')
         while loc < len(self.code):
             matched = False
             value = None
@@ -139,10 +140,9 @@ def lex(self, subdir):
                     elif tid == 'dblquote':
                         raise ParseException('Double quotes are not supported. Use single quotes.', self.getline(line_start), lineno, col)
                     elif tid == 'string':
-                        value = match_text[1:-1]\
-                            .replace(r"\'", "'")\
-                            .replace(r" \\ ".strip(), r" \ ".strip())\
-                            .replace("\\n", "\n")
+                        value = match_text[1:-1].replace(r"\'", "'")
+                        value = newline_rx.sub(r'\1\n', value)
+                        value = value.replace(r" \\ ".strip(), r" \ ".strip())
                     elif tid == 'multiline_string':
                         tid = 'string'
                         value = match_text[3:-3]


Test patch:
diff --git a/test cases/common/42 string operations/meson.build b/test cases/common/42 string operations/meson.build
--- a/test cases/common/42 string operations/meson.build	
+++ b/test cases/common/42 string operations/meson.build	
@@ -74,3 +74,24 @@ multiline string	'''.strip() == '''multiline string''', 'Newlines badly stripped
 assert('"1.1.20"'.strip('"') == '1.1.20', '" badly stripped')
 assert('"1.1.20"'.strip('".') == '1.1.20', '". badly stripped')
 assert('"1.1.20"   '.strip('" ') == '1.1.20', '". badly stripped')
+
+bs_b = '''\b'''
+bs_bs_b = '''\\b'''
+nl = '''
+'''
+bs_n = '''\n'''
+bs_nl = '''\
+'''
+bs_bs_n = '''\\n'''
+bs_bs_nl = '''\\
+'''
+
+assert('\b' == bs_b, 'Single backslash broken')
+assert('\\b' == bs_b, 'Double backslash broken')
+assert('\\\b' == bs_bs_b, 'Three backslash broken')
+assert('\\\\b' == bs_bs_b, 'Four backslash broken')
+assert('\n' == nl, 'Newline escape broken')
+assert('\\n' == bs_n, 'Double backslash broken before n')
+assert('\\\n' == bs_nl, 'Three backslash broken before n')
+assert('\\\\n' == bs_bs_n, 'Four backslash broken before n')
+assert('\\\\\n' == bs_bs_nl, 'Five backslash broken before n')