diff options
-rw-r--r-- | docs/markdown/Syntax.md | 6 | ||||
-rw-r--r-- | docs/markdown/snippets/support-multiline-fstring.md | 22 | ||||
-rw-r--r-- | mesonbuild/interpreterbase/interpreterbase.py | 9 | ||||
-rw-r--r-- | mesonbuild/mparser.py | 23 | ||||
-rw-r--r-- | test cases/common/62 string arithmetic/meson.build | 43 | ||||
-rw-r--r-- | test cases/common/62 string arithmetic/test.json | 7 |
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." + } + ] +} |