aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Syntax.md6
-rw-r--r--docs/markdown/snippets/support-multiline-fstring.md22
-rw-r--r--mesonbuild/interpreterbase/interpreterbase.py9
-rw-r--r--mesonbuild/mparser.py23
-rw-r--r--test cases/common/62 string arithmetic/meson.build43
-rw-r--r--test cases/common/62 string arithmetic/test.json7
6 files changed, 101 insertions, 9 deletions
diff --git a/docs/markdown/Syntax.md b/docs/markdown/Syntax.md
index b3b328f..a942af0 100644
--- a/docs/markdown/Syntax.md
+++ b/docs/markdown/Syntax.md
@@ -178,7 +178,7 @@ These are raw strings that do not support the escape sequences listed
above. These strings can also be combined with the string formatting
functionality via `.format()` described below.
-Note that multiline f-strings are not supported.
+Note that multiline f-string support was added in version 0.63.
### String index
@@ -211,8 +211,8 @@ As can be seen, the formatting works by replacing placeholders of type
*(Added 0.58)*
Format strings can be used as a non-positional alternative to the
-string formatting functionality described above. Note that multiline f-strings
-are not supported.
+string formatting functionality described above. Note that multiline f-string
+support was added in version 0.63.
```meson
n = 10
diff --git a/docs/markdown/snippets/support-multiline-fstring.md b/docs/markdown/snippets/support-multiline-fstring.md
new file mode 100644
index 0000000..296a04e
--- /dev/null
+++ b/docs/markdown/snippets/support-multiline-fstring.md
@@ -0,0 +1,22 @@
+## Added support for multiline fstrings
+
+Added support for multiline f-strings which use the same syntax as f-strings
+for string substition.
+
+```meson
+x = 'hello'
+y = 'world'
+
+msg = f'''Sending a message...
+"@x@ @y@"
+'''
+```
+
+which produces:
+
+```
+Sending a message....
+
+"hello world"
+
+```
diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py
index 85aabd1..f9a3b07 100644
--- a/mesonbuild/interpreterbase/interpreterbase.py
+++ b/mesonbuild/interpreterbase/interpreterbase.py
@@ -217,7 +217,10 @@ class InterpreterBase:
elif isinstance(cur, mparser.TernaryNode):
return self.evaluate_ternary(cur)
elif isinstance(cur, mparser.FormatStringNode):
- return self.evaluate_fstring(cur)
+ if isinstance(cur, mparser.MultilineFormatStringNode):
+ return self.evaluate_multiline_fstring(cur)
+ else:
+ return self.evaluate_fstring(cur)
elif isinstance(cur, mparser.ContinueNode):
raise ContinueRequest()
elif isinstance(cur, mparser.BreakNode):
@@ -367,6 +370,10 @@ class InterpreterBase:
else:
return self.evaluate_statement(node.falseblock)
+ @FeatureNew('multiline format strings', '0.63.0')
+ def evaluate_multiline_fstring(self, node: mparser.MultilineFormatStringNode) -> InterpreterObject:
+ return self.evaluate_fstring(node)
+
@FeatureNew('format strings', '0.58.0')
def evaluate_fstring(self, node: mparser.FormatStringNode) -> InterpreterObject:
assert isinstance(node, mparser.FormatStringNode)
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index 97a87d8..0995ee8 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -114,6 +114,7 @@ class Lexer:
self.token_specification = [
# Need to be sorted longest to shortest.
('ignore', re.compile(r'[ \t]')),
+ ('multiline_fstring', re.compile(r"f'''(.|\n)*?'''", re.M)),
('fstring', re.compile(r"f'([^'\\]|(\\.))*'")),
('id', re.compile('[_a-zA-Z][_0-9a-zA-Z]*')),
('number', re.compile(r'0[bB][01]+|0[oO][0-7]+|0[xX][0-9a-fA-F]+|0|[1-9]\d*')),
@@ -203,9 +204,17 @@ class Lexer:
value = ESCAPE_SEQUENCE_SINGLE_RE.sub(decode_match, value)
except MesonUnicodeDecodeError as err:
raise MesonException(f"Failed to parse escape sequence: '{err.match}' in string:\n {match_text}")
- elif tid == 'multiline_string':
- tid = 'string'
- value = match_text[3:-3]
+ elif tid in {'multiline_string', 'multiline_fstring'}:
+ # For multiline strings, parse out the value and pass
+ # through the normal string logic.
+ # For multiline format strings, we have to emit a
+ # different AST node so we can add a feature check,
+ # but otherwise, it follows the normal fstring logic.
+ if tid == 'multiline_string':
+ value = match_text[3:-3]
+ tid = 'string'
+ else:
+ value = match_text[4:-3]
lines = match_text.split('\n')
if len(lines) > 1:
lineno += len(lines) - 1
@@ -298,7 +307,11 @@ class FormatStringNode(ElementaryNode[str]):
assert isinstance(self.value, str)
def __str__(self) -> str:
- return "Format string node: '{self.value}' ({self.lineno}, {self.colno})."
+ return f"Format string node: '{self.value}' ({self.lineno}, {self.colno})."
+
+class MultilineFormatStringNode(FormatStringNode):
+ def __str__(self) -> str:
+ return f"Multiline Format string node: '{self.value}' ({self.lineno}, {self.colno})."
class ContinueNode(ElementaryNode):
pass
@@ -685,6 +698,8 @@ class Parser:
return StringNode(t)
if self.accept('fstring'):
return FormatStringNode(t)
+ if self.accept('multiline_fstring'):
+ return MultilineFormatStringNode(t)
return EmptyNode(self.current.lineno, self.current.colno, self.current.filename)
def key_values(self) -> ArgumentNode:
diff --git a/test cases/common/62 string arithmetic/meson.build b/test cases/common/62 string arithmetic/meson.build
index 8ef350e..194df0d 100644
--- a/test cases/common/62 string arithmetic/meson.build
+++ b/test cases/common/62 string arithmetic/meson.build
@@ -1,4 +1,4 @@
-project('string arithmetic', 'c')
+project('string arithmetic', 'c', meson_version: '>=0.62.0')
assert('foo' + 'bar' == 'foobar')
assert('foo' + 'bar' + 'baz' == 'foobarbaz')
@@ -6,3 +6,44 @@ assert('foo' + 'bar' + 'baz' == 'foobarbaz')
a = 'a'
b = 'b'
assert(a + b + 'c' == 'abc')
+
+# ------------------------------------------------------------------------------
+# format strings:
+# ------------------------------------------------------------------------------
+sub1 = 'the'
+sub2 = ' quick\n'
+sub3 = ' brown'
+sub4 = '\nfox'
+x = f'@sub1@@sub2@@sub3@@sub4@'
+
+assert(x == sub1 + sub2 + sub3 + sub4)
+assert(x == 'the quick\n brown\nfox')
+
+# ------------------------------------------------------------------------------
+# multi-line format strings
+# ------------------------------------------------------------------------------
+y_actual = f'''This is a multi-line comment with string substition:
+ "@sub1@@sub2@@sub3@@sub4@"
+
+And I can even substitute the entry multiple times!
+
+@sub1@
+@sub2@
+@sub3@
+'''
+
+y_expect = '''This is a multi-line comment with string substition:
+ "the quick
+ brown
+fox"
+
+And I can even substitute the entry multiple times!
+
+the
+ quick
+
+ brown
+'''
+message('actual=' + y_actual)
+message('expect=' + y_expect)
+assert(y_actual == y_expect)
diff --git a/test cases/common/62 string arithmetic/test.json b/test cases/common/62 string arithmetic/test.json
new file mode 100644
index 0000000..e3519d0
--- /dev/null
+++ b/test cases/common/62 string arithmetic/test.json
@@ -0,0 +1,7 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/common/62 string arithmetic/meson.build:25: WARNING: Project targeting '>=0.62.0' but tried to use feature introduced in '0.63.0': multiline format strings."
+ }
+ ]
+}