Add support for f-strings to PTL compiler.

This commit is contained in:
Neil Schemenauer 2016-12-09 12:55:48 -08:00
parent 5e147894d4
commit fad65207ef
2 changed files with 76 additions and 1 deletions

View File

@ -48,6 +48,8 @@ except ImportError:
from quixote.html._py_htmltext import htmltext, htmlescape, \
stringify, TemplateIO
from quixote.html._py_htmltext import _wraparg
ValuelessAttr = object() # magic singleton object
def htmltag(tag, xml_end=False, css_class=None, **attrs):
@ -106,6 +108,34 @@ def url_quote(value, fallback=None):
return fallback
return urllib.parse.quote(stringify(value))
def _q_join(*args):
# Used by f-strings to join the {..} parts
return htmltext('').join(args)
def _q_format(value, conversion=-1, format_spec=None):
# Used by f-strings to format the {..} parts
if conversion == -1 and format_spec is None:
return htmlescape(value) # simple and fast case
if conversion == -1:
fmt = '{%s}'
else:
conversion = chr(conversion)
if conversion == 'r':
fmt = '{%s!r}'
elif conversion == 's':
fmt = '{%s!s}'
elif conversion == 'a':
fmt = '{%s!a}'
else:
assert 0, 'invalid conversion %r' % conversion
arg = _wraparg(value)
if format_spec:
fmt = fmt % (':' + str(format_spec))
else:
fmt = fmt % ''
return htmltext(fmt.format(arg))
_saved = None
def use_qpy():
"""

View File

@ -35,7 +35,12 @@ class TemplateTransformer(ast.NodeTransformer):
names=[ast.alias(name='TemplateIO',
asname='_q_TemplateIO'),
ast.alias(name='htmltext',
asname='_q_htmltext')],
asname='_q_htmltext'),
ast.alias(name='_q_join',
asname='_q_join'),
ast.alias(name='_q_format',
asname='_q_format'),
],
level=0)
ast.fix_missing_locations(html_imp)
vars_imp = ast.ImportFrom(module='builtins',
@ -116,6 +121,46 @@ class TemplateTransformer(ast.NodeTransformer):
else:
return node
def visit_JoinedStr(self, node):
# JoinedStr is used for combining the parts of an f-string.
# In CPython, it is done with the BUILD_STRING opcode. We
# call quixote.html._q_join() instead
node = self.generic_visit(node)
if "html" == self._get_template_type():
n = ast.Name(id='_q_join', ctx=ast.Load())
n = ast.Call(func=n, args=node.values, keywords=[],
starargs=None,
kwargs=None)
ast.copy_location(n, node)
ast.fix_missing_locations(n)
return n
else:
return node
def visit_FormattedValue(self, node):
# FormattedValue is used for the {..} parts of an f-string.
# In CPython, there is a FORMAT_VALUE opcode. We call
# quixote.html._q_format instead.
node = self.generic_visit(node)
if "html" == self._get_template_type():
n = ast.Name(id='_q_format', ctx=ast.Load())
conversion = ast.copy_location(ast.Num(node.conversion), node)
args = [node.value]
if node.format_spec is not None:
args += [conversion, node.format_spec]
elif node.conversion != -1:
args += [conversion]
n = ast.Call(func=n, args=args,
keywords=[],
starargs=None,
kwargs=None)
ast.copy_location(n, node)
ast.fix_missing_locations(n)
return n
else:
return node
_template_re = re.compile(r'''
^(?P<indent>[ \t]*) def (?:[ \t]+)
(?P<name>[a-zA-Z_][a-zA-Z_0-9]*)