<div dir="ltr"><div class="gmail_default" style="color:rgb(0,0,0)"><br></div><div class="gmail_extra"><div class="gmail_quote">On Sat, Nov 5, 2016 at 10:36 AM, Nick Coghlan <span dir="ltr"><<a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="gmail-">On 5 November 2016 at 04:03, Fabio Zadrozny <<a href="mailto:fabiofz@gmail.com">fabiofz@gmail.com</a>> wrote:<br>
> On Fri, Nov 4, 2016 at 3:15 PM, Eric V. Smith <<a href="mailto:eric@trueblade.com">eric@trueblade.com</a>> wrote:<br>
</span><span class="gmail-">>> Using PyParser_ASTFromString is the easiest possible way to do this. Given<br>
>> a string, it returns an AST node. What could be simpler?<br>
><br>
><br>
> I think that for implementation purposes, given the python infrastructure,<br>
> it's fine, but for specification purposes, probably incorrect... As I don't<br>
> think f-strings should accept:<br>
><br>
>  f"start {import sys; sys.version_info[0];} end" (i.e.:<br>
> PyParser_ASTFromString doesn't just return an expression, it accepts any<br>
> valid Python code, even code which can't be used in an f-string).<br>
<br>
</span>f-strings use the "eval" parsing mode, which starts from the<br>
"eval_input" node in the grammar (which is only a couple of nodes<br>
higher than 'test', allowing tuples via 'testlist' as well as trailing<br>
newlines and EOF):<br>
<br>
    >>> ast.parse("import sys; sys.version_info[0];", mode="eval")<br>
    Traceback (most recent call last):<br>
     File "<stdin>", line 1, in <module><br>
     File "/usr/lib64/python3.5/ast.py", line 35, in parse<br>
       return compile(source, filename, mode, PyCF_ONLY_AST)<br>
     File "<example>", line 1<br>
       import sys; sys.version_info[0];<br>
            ^<br>
    SyntaxError: invalid syntax<br>
<br>
You have to use "exec" mode to get the parser to allow statements,<br>
which is why f-strings don't do that:<br>
<br>
    >>> ast.dump(ast.parse("import sys; sys.version_info[0];", mode="exec"))<br>
    "Module(body=[Import(names=[<wbr>alias(name='sys', asname=None)]),<br>
Expr(value=Subscript(value=<wbr>Attribute(value=Name(id='sys', ctx=Load()),<br>
attr='version_info', ctx=Load()), slice=Index(value=Num(n=0)),<br>
ctx=Load()))])"<br>
<br>
The unique aspect for f-strings that means they don't permit some<br>
otherwise valid Python expressions is that it also does the initial<br>
pre-tokenisation based on:<br>
<br>
1. Look for an opening '{'<br>
2. Look for a closing '!', ':' or '}'  accounting for balanced string<br>
quotes, parentheses, brackets and braces<br>
<br>
Ignoring the surrounding quotes, and using the `atom` node from<br>
Python's grammar to represent the nesting tracking, and TEXT to stand<br>
in for arbitrary text, it's something akin to:<br>
<br>
    fstring: (TEXT ['{' maybe_pyexpr ('!' | ':' | '}')])+<br>
    maybe_pyexpr: (atom | TEXT)+<br>
<br>
That isn't quite right, since it doesn't properly account for brace<br>
nesting, but it gives the general idea - there's an initial really<br>
simple tokenising pass that picks out the potential Python<br>
expressions, and then those are run through the AST parser's<br>
equivalent of eval().<br>
<br>
Cheers,<br>
Nick.<br>
<span class="gmail-HOEnZb"><font color="#888888"><br>
--<br>
Nick Coghlan   |   <a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>   |   Brisbane, Australia<br>
</font></span></blockquote></div><br></div><div class="gmail_extra"><br></div><div class="gmail_extra"><div class="gmail_default" style="color:rgb(0,0,0)">​Hi Nick and Eric,</div><div class="gmail_default" style="color:rgb(0,0,0)"><br></div><div class="gmail_default" style="color:rgb(0,0,0)">Just wanted to say thanks for the feedback and point to a grammar I ended up doing on my side (in JavaCC), just in case someone else decides to do a formal grammar later on it can probably be used as a reference (shouldn't be hard to convert it to a bnf grammar):</div><div class="gmail_default" style="color:rgb(0,0,0)"><br></div><div class="gmail_default" style="color:rgb(0,0,0)"><a href="https://github.com/fabioz/Pydev/blob/master/plugins/org.python.pydev.parser/src/org/python/pydev/parser/grammar_fstrings/grammar_fstrings.jjt">https://github.com/fabioz/Pydev/blob/master/plugins/org.python.pydev.parser/src/org/python/pydev/parser/grammar_fstrings/grammar_fstrings.jjt</a>​</div><div class="gmail_default" style="color:rgb(0,0,0)"><br></div><div class="gmail_default" style="color:rgb(0,0,0)">Also, as a feedback, I found it a bit odd that there can't be any space nor new line between the last format specifiers and '}'</div><div class="gmail_default" style="color:rgb(0,0,0)"><br></div><div class="gmail_default" style="color:rgb(0,0,0)">I.e.:</div><div class="gmail_default" style="color:rgb(0,0,0)"><br></div><div class="gmail_default" style="color:rgb(0,0,0)">f'''{</div><div class="gmail_default" style="color:rgb(0,0,0)">dict(</div><div class="gmail_default" style="color:rgb(0,0,0)">  a = 10</div><div class="gmail_default" style="color:rgb(0,0,0)">)</div><div class="gmail_default" style="color:rgb(0,0,0)">!r</div><div class="gmail_default" style="color:rgb(0,0,0)">}</div><div class="gmail_default" style="color:rgb(0,0,0)">'''</div><br></div><div class="gmail_extra"><div class="gmail_default" style="color:rgb(0,0,0)">​is not valid, whereas ​</div><br></div><div class="gmail_extra"><div class="gmail_default" style="color:rgb(0,0,0)">​</div><div class="gmail_default" style="color:rgb(0,0,0)">f'''{</div><div class="gmail_default" style="color:rgb(0,0,0)">dict(</div><div class="gmail_default" style="color:rgb(0,0,0)">  a = 10</div><div class="gmail_default" style="color:rgb(0,0,0)">)</div><div class="gmail_default" style="color:rgb(0,0,0)">!r}</div><div class="gmail_default" style="color:rgb(0,0,0)">'''​</div><div class="gmail_default" style="color:rgb(0,0,0)">is valid -- as a note, this means my grammar has a bug as both versions are accepted -- and I currently don't care enough about that change from the implementation to fix it ;)</div><div class="gmail_default" style="color:rgb(0,0,0)"><br></div></div><div class="gmail_extra"><div class="gmail_default" style="color:rgb(0,0,0)">Cheers,</div><div class="gmail_default" style="color:rgb(0,0,0)"><br></div><div class="gmail_default" style="color:rgb(0,0,0)">Fabio​</div><br></div></div>