diff --git a/uncompyle6/semantics/customize3.py b/uncompyle6/semantics/customize3.py
index 2d8759d5..27922ef8 100644
--- a/uncompyle6/semantics/customize3.py
+++ b/uncompyle6/semantics/customize3.py
@@ -16,15 +16,14 @@
"""Isolate Python 3 version-specific semantic actions here.
"""
-from uncompyle6.semantics.consts import (
- INDENT_PER_LEVEL, PRECEDENCE, TABLE_DIRECT, TABLE_R)
+from uncompyle6.semantics.consts import TABLE_DIRECT
from xdis.code import iscode
-from xdis.util import COMPILER_FLAG_BIT
-from spark_parser.ast import GenericASTTraversalPruningException
-from uncompyle6.scanners.tok import Token
-from uncompyle6.semantics.helper import flatten_list
from uncompyle6.semantics.make_function import make_function3_annotate
+from uncompyle6.semantics.customize35 import customize_for_version35
+from uncompyle6.semantics.customize36 import customize_for_version36
+from uncompyle6.semantics.customize37 import customize_for_version37
+from uncompyle6.semantics.customize38 import customize_for_version38
def customize_for_version3(self, version):
TABLE_DIRECT.update({
@@ -276,747 +275,17 @@ def customize_for_version3(self, version):
TABLE_DIRECT.update({
'LOAD_CLASSDEREF': ( '%{pattr}', ),
})
-
- ########################
- # Python 3.5+ Additions
- #######################
if version >= 3.5:
- TABLE_DIRECT.update({
- 'await_expr': ( 'await %c', 0),
- 'await_stmt': ( '%|%c\n', 0),
- 'async_for_stmt': (
- '%|async for %c in %c:\n%+%|%c%-\n\n', 9, 1, 25 ),
- 'async_forelse_stmt': (
- '%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
- 9, 1, 25, (27, 'else_suite') ),
- 'async_with_stmt': (
- '%|async with %c:\n%+%|%c%-',
- (0, 'expr'), 7 ),
- 'async_with_as_stmt': (
- '%|async with %c as %c:\n%+%|%c%-',
- (0, 'expr'), (6, 'store'), 7),
- 'unmap_dict': ( '{**%C}', (0, -1, ', **') ),
- # 'unmapexpr': ( '{**%c}', 0), # done by n_unmapexpr
-
- })
-
- def async_call(node):
- self.f.write('async ')
- node.kind == 'call'
- p = self.prec
- self.prec = 80
- self.template_engine(('%c(%P)', 0, (1, -4, ', ',
- 100)), node)
- self.prec = p
- node.kind == 'async_call'
- self.prune()
- self.n_async_call = async_call
- self.n_build_list_unpack = self.n_list
-
- if version == 3.5:
- def n_call(node):
- mapping = self._get_mapping(node)
- table = mapping[0]
- key = node
- for i in mapping[1:]:
- key = key[i]
- pass
- if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
- # Python 3.5 changes the stack position of
- # *args: kwargs come after *args whereas
- # in earlier Pythons, *args is at the end
- # which simplifies things from our
- # perspective. Python 3.6+ replaces
- # CALL_FUNCTION_VAR_KW with
- # CALL_FUNCTION_EX We will just swap the
- # order to make it look like earlier
- # Python 3.
- entry = table[key.kind]
- kwarg_pos = entry[2][1]
- args_pos = kwarg_pos - 1
- # Put last node[args_pos] after subsequent kwargs
- while node[kwarg_pos] == 'kwarg' and kwarg_pos < len(node):
- # swap node[args_pos] with node[kwargs_pos]
- node[kwarg_pos], node[args_pos] = node[args_pos], node[kwarg_pos]
- args_pos = kwarg_pos
- kwarg_pos += 1
- elif key.kind.startswith('CALL_FUNCTION_VAR'):
- # CALL_FUNCTION_VAR's top element of the stack contains
- # the variable argument list, then comes
- # annotation args, then keyword args.
- # In the most least-top-most stack entry, but position 1
- # in node order, the positional args.
- argc = node[-1].attr
- nargs = argc & 0xFF
- kwargs = (argc >> 8) & 0xFF
- # FIXME: handle annotation args
- if nargs > 0:
- template = ('%c(%C, ', 0, (1, nargs+1, ', '))
- else:
- template = ('%c(', 0)
- self.template_engine(template, node)
-
- args_node = node[-2]
- if args_node in ('pos_arg', 'expr'):
- args_node = args_node[0]
- if args_node == 'build_list_unpack':
- template = ('*%P)', (0, len(args_node)-1, ', *', 100))
- self.template_engine(template, args_node)
- else:
- if len(node) - nargs > 3:
- template = ('*%c, %C)', nargs+1, (nargs+kwargs+1, -1, ', '))
- else:
- template = ('*%c)', nargs+1)
- self.template_engine(template, node)
- self.prune()
-
- self.default(node)
- self.n_call = n_call
-
- def n_function_def(node):
- if self.version >= 3.6:
- code_node = node[0][0]
- else:
- code_node = node[0][1]
-
- is_code = hasattr(code_node, 'attr') and iscode(code_node.attr)
- if (is_code and
- (code_node.attr.co_flags & COMPILER_FLAG_BIT['COROUTINE'])):
- self.template_engine(('\n\n%|async def %c\n',
- -2), node)
- else:
- self.template_engine(('\n\n%|def %c\n', -2),
- node)
- self.prune()
- self.n_function_def = n_function_def
-
- def unmapexpr(node):
- last_n = node[0][-1]
- for n in node[0]:
- self.preorder(n)
- if n != last_n:
- self.f.write(', **')
- pass
- pass
- self.prune()
- pass
- self.n_unmapexpr = unmapexpr
-
- # FIXME: start here
- def n_list_unpack(node):
- """
- prettyprint an unpacked list or tuple
- """
- p = self.prec
- self.prec = 100
- lastnode = node.pop()
- lastnodetype = lastnode.kind
-
- # If this build list is inside a CALL_FUNCTION_VAR,
- # then the first * has already been printed.
- # Until I have a better way to check for CALL_FUNCTION_VAR,
- # will assume that if the text ends in *.
- last_was_star = self.f.getvalue().endswith('*')
-
- if lastnodetype.startswith('BUILD_LIST'):
- self.write('['); endchar = ']'
- elif lastnodetype.startswith('BUILD_TUPLE'):
- # Tuples can appear places that can NOT
- # have parenthesis around them, like array
- # subscripts. We check for that by seeing
- # if a tuple item is some sort of slice.
- no_parens = False
- for n in node:
- if n == 'expr' and n[0].kind.startswith('build_slice'):
- no_parens = True
- break
- pass
- if no_parens:
- endchar = ''
- else:
- self.write('('); endchar = ')'
- pass
-
- elif lastnodetype.startswith('BUILD_SET'):
- self.write('{'); endchar = '}'
- elif lastnodetype.startswith('BUILD_MAP_UNPACK'):
- self.write('{*'); endchar = '}'
- elif lastnodetype.startswith('ROT_TWO'):
- self.write('('); endchar = ')'
- else:
- raise TypeError('Internal Error: n_build_list expects list, tuple, set, or unpack')
-
- flat_elems = flatten_list(node)
-
- self.indent_more(INDENT_PER_LEVEL)
- sep = ''
- for elem in flat_elems:
- if elem in ('ROT_THREE', 'EXTENDED_ARG'):
- continue
- assert elem == 'expr'
- line_number = self.line_number
- value = self.traverse(elem)
- if elem[0] == 'tuple':
- assert value[0] == '('
- assert value[-1] == ')'
- value = value[1:-1]
- if value[-1] == ',':
- # singleton tuple
- value = value[:-1]
- else:
- value = '*' + value
- if line_number != self.line_number:
- sep += '\n' + self.indent + INDENT_PER_LEVEL[:-1]
- else:
- if sep != '': sep += ' '
- if not last_was_star:
- pass
- else:
- last_was_star = False
- self.write(sep, value)
- sep = ','
- if lastnode.attr == 1 and lastnodetype.startswith('BUILD_TUPLE'):
- self.write(',')
- self.write(endchar)
- self.indent_less(INDENT_PER_LEVEL)
-
- self.prec = p
- self.prune()
- return
-
- self.n_tuple_unpack = n_list_unpack
-
-
- if version >= 3.6:
- ########################
- # Python 3.6+ Additions
- #######################
-
- # Value 100 is important; it is exactly
- # module/function precidence.
- PRECEDENCE['call_kw'] = 100
- PRECEDENCE['call_kw36'] = 100
- PRECEDENCE['call_ex'] = 100
- PRECEDENCE['call_ex_kw'] = 100
- PRECEDENCE['call_ex_kw2'] = 100
- PRECEDENCE['call_ex_kw3'] = 100
- PRECEDENCE['call_ex_kw4'] = 100
- PRECEDENCE['unmap_dict'] = 0
-
- TABLE_DIRECT.update({
- 'tryfinally36': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
- (1, 'returns'), 3 ),
- 'fstring_expr': ( "{%c%{conversion}}", 0),
- # FIXME: the below assumes the format strings
- # don't have ''' in them. Fix this properly
- 'fstring_single': ( "f'''{%c%{conversion}}'''", 0),
- 'fstring_multi': ( "f'''%c'''", 0),
- 'func_args36': ( "%c(**", 0),
- 'try_except36': ( '%|try:\n%+%c%-%c\n\n', 1, 2 ),
- 'except_return': ( '%|except:\n%+%c%-', 3 ),
- 'unpack_list': ( '*%c', (0, 'list') ),
- 'tryfinally_return_stmt':
- ( '%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n', 1 ),
-
- 'async_for_stmt36': (
- '%|async for %c in %c:\n%+%c%-%-\n\n',
- (9, 'store'), (1, 'expr'), (18, 'for_block') ),
-
- 'call_ex' : (
- '%c(%p)',
- (0, 'expr'), (1, 100)),
- 'call_ex_kw' : (
- '%c(%p)',
- (0, 'expr'), (2, 100)),
-
- })
-
- TABLE_R.update({
- 'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)),
- # Not quite right
- 'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2, 3, ',')),
- })
-
- def build_unpack_tuple_with_call(node):
-
- if node[0] == 'expr':
- tup = node[0][0]
- else:
- tup = node[0]
- pass
- assert tup == 'tuple'
- self.call36_tuple(tup)
-
- buwc = node[-1]
- assert buwc.kind.startswith('BUILD_TUPLE_UNPACK_WITH_CALL')
- for n in node[1:-1]:
- self.f.write(', *')
- self.preorder(n)
- pass
- self.prune()
- return
- self.n_build_tuple_unpack_with_call = build_unpack_tuple_with_call
-
- def build_unpack_map_with_call(node):
- n = node[0]
- if n == 'expr':
- n = n[0]
- if n == 'dict':
- self.call36_dict(n)
- first = 1
- sep = ', **'
- else:
- first = 0
- sep = '**'
- for n in node[first:-1]:
- self.f.write(sep)
- self.preorder(n)
- sep = ', **'
- pass
- self.prune()
- return
- self.n_build_map_unpack_with_call = build_unpack_map_with_call
-
- def call_ex_kw2(node):
- """Handle CALL_FUNCTION_EX 2 (have KW) but with
- BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""
-
- # This is weird shit. Thanks Python!
- self.preorder(node[0])
- self.write('(')
-
- assert node[1] == 'build_tuple_unpack_with_call'
- btuwc = node[1]
- tup = btuwc[0]
- if tup == 'expr':
- tup = tup[0]
- assert tup == 'tuple'
- self.call36_tuple(tup)
- assert node[2] == 'build_map_unpack_with_call'
-
- self.write(', ')
- d = node[2][0]
- if d == 'expr':
- d = d[0]
- assert d == 'dict'
- self.call36_dict(d)
-
- args = btuwc[1]
- self.write(', *')
- self.preorder(args)
-
- self.write(', **')
- star_star_args = node[2][1]
- if star_star_args == 'expr':
- star_star_args = star_star_args[0]
- self.preorder(star_star_args)
- self.write(')')
- self.prune()
- self.n_call_ex_kw2 = call_ex_kw2
-
- def call_ex_kw3(node):
- """Handle CALL_FUNCTION_EX 1 (have KW) but without
- BUILD_MAP_UNPACK_WITH_CALL"""
- self.preorder(node[0])
- self.write('(')
- args = node[1][0]
- if args == 'expr':
- args = args[0]
- if args == 'tuple':
- if self.call36_tuple(args) > 0:
- self.write(', ')
- pass
- pass
-
- self.write('*')
- self.preorder(node[1][1])
- self.write(', ')
-
- kwargs = node[2]
- if kwargs == 'expr':
- kwargs = kwargs[0]
- if kwargs == 'dict':
- self.call36_dict(kwargs)
- else:
- self.write('**')
- self.preorder(kwargs)
- self.write(')')
- self.prune()
- self.n_call_ex_kw3 = call_ex_kw3
-
- def call_ex_kw4(node):
- """Handle CALL_FUNCTION_EX {1 or 2} but without
- BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""
- self.preorder(node[0])
- self.write('(')
- args = node[1][0]
- if args == 'tuple':
- if self.call36_tuple(args) > 0:
- self.write(', ')
- pass
- pass
- else:
- self.write('*')
- self.preorder(args)
- self.write(', ')
- pass
-
- kwargs = node[2]
- if kwargs == 'expr':
- kwargs = kwargs[0]
- call_function_ex = node[-1]
- assert (call_function_ex == 'CALL_FUNCTION_EX_KW'
- or (self.version >= 3.6 and call_function_ex == 'CALL_FUNCTION_EX'))
- # FIXME: decide if the below test be on kwargs == 'dict'
- if (call_function_ex.attr & 1 and
- (not isinstance(kwargs, Token) and kwargs != 'attribute')
- and not kwargs[0].kind.startswith('kvlist')):
- self.call36_dict(kwargs)
- else:
- self.write('**')
- self.preorder(kwargs)
- self.write(')')
- self.prune()
- self.n_call_ex_kw4 = call_ex_kw4
-
- def call36_tuple(node):
- """
- A tuple used in a call, these are like normal tuples but they
- don't have the enclosing parenthesis.
- """
- assert node == 'tuple'
- # Note: don't iterate over last element which is a
- # BUILD_TUPLE...
- flat_elems = flatten_list(node[:-1])
-
- self.indent_more(INDENT_PER_LEVEL)
- sep = ''
-
- for elem in flat_elems:
- if elem in ('ROT_THREE', 'EXTENDED_ARG'):
- continue
- assert elem == 'expr'
- line_number = self.line_number
- value = self.traverse(elem)
- if line_number != self.line_number:
- sep += '\n' + self.indent + INDENT_PER_LEVEL[:-1]
- self.write(sep, value)
- sep = ', '
-
- self.indent_less(INDENT_PER_LEVEL)
- return len(flat_elems)
- self.call36_tuple = call36_tuple
-
- def call36_dict(node):
- """
- A dict used in a call_ex_kw2, which are a dictionary items expressed
- in a call. This should format to:
- a=1, b=2
- In other words, no braces, no quotes around keys and ":" becomes
- "=".
-
- We will source-code use line breaks to guide us when to break.
- """
- p = self.prec
- self.prec = 100
-
- self.indent_more(INDENT_PER_LEVEL)
- sep = INDENT_PER_LEVEL[:-1]
- line_number = self.line_number
-
- if node[0].kind.startswith('kvlist'):
- # Python 3.5+ style key/value list in dict
- kv_node = node[0]
- l = list(kv_node)
- i = 0
-
- length = len(l)
- # FIXME: Parser-speed improved grammars will have BUILD_MAP
- # at the end. So in the future when everything is
- # complete, we can do an "assert" instead of "if".
- if kv_node[-1].kind.startswith("BUILD_MAP"):
- length -= 1
-
- # Respect line breaks from source
- while i < length:
- self.write(sep)
- name = self.traverse(l[i], indent='')
- # Strip off beginning and trailing quotes in name
- name = name[1:-1]
- if i > 0:
- line_number = self.indent_if_source_nl(line_number,
- self.indent + INDENT_PER_LEVEL[:-1])
- line_number = self.line_number
- self.write(name, '=')
- value = self.traverse(l[i+1], indent=self.indent+(len(name)+2)*' ')
- self.write(value)
- sep = ", "
- if line_number != self.line_number:
- sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
- line_number = self.line_number
- i += 2
- pass
- elif node[-1].kind.startswith('BUILD_CONST_KEY_MAP'):
- keys_node = node[-2]
- keys = keys_node.attr
- # from trepan.api import debug; debug()
- assert keys_node == 'LOAD_CONST' and isinstance(keys, tuple)
- for i in range(node[-1].attr):
- self.write(sep)
- self.write(keys[i], '=')
- value = self.traverse(node[i], indent='')
- self.write(value)
- sep = ", "
- if line_number != self.line_number:
- sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
- line_number = self.line_number
- pass
- pass
- else:
- self.write("**")
- try:
- self.default(node)
- except GenericASTTraversalPruningException:
- pass
-
- self.prec = p
- self.indent_less(INDENT_PER_LEVEL)
- return
- self.call36_dict = call36_dict
-
-
- FSTRING_CONVERSION_MAP = {1: '!s', 2: '!r', 3: '!a'}
-
- def n_except_suite_finalize(node):
- if node[1] == 'returns' and self.hide_internal:
- # Process node[1] only.
- # The code after "returns", e.g. node[3], is dead code.
- # Adding it is wrong as it dedents and another
- # exception handler "except_stmt" afterwards.
- # Note it is also possible that the grammar is wrong here.
- # and this should not be "except_stmt".
- self.indent_more()
- self.preorder(node[1])
- self.indent_less()
- else:
- self.default(node)
- self.prune()
- self.n_except_suite_finalize = n_except_suite_finalize
-
- def n_formatted_value(node):
- if node[0] == 'LOAD_CONST':
- self.write(node[0].attr)
- self.prune()
- else:
- self.default(node)
- self.n_formatted_value = n_formatted_value
-
- def f_conversion(node):
- node.conversion = FSTRING_CONVERSION_MAP.get(node.data[1].attr, '')
-
- def fstring_expr(node):
- f_conversion(node)
- self.default(node)
- self.n_fstring_expr = fstring_expr
-
- def fstring_single(node):
- f_conversion(node)
- self.default(node)
- self.n_fstring_single = fstring_single
-
- # def kwargs_only_36(node):
- # keys = node[-1].attr
- # num_kwargs = len(keys)
- # values = node[:num_kwargs]
- # for i, (key, value) in enumerate(zip(keys, values)):
- # self.write(key + '=')
- # self.preorder(value)
- # if i < num_kwargs:
- # self.write(',')
- # self.prune()
- # return
- # self.n_kwargs_only_36 = kwargs_only_36
-
- def n_call_kw36(node):
- self.template_engine(("%c(", 0), node)
- keys = node[-2].attr
- num_kwargs = len(keys)
- num_posargs = len(node) - (num_kwargs + 2)
- n = len(node)
- assert n >= len(keys)+1, \
- 'not enough parameters keyword-tuple values'
- sep = ''
-
- line_number = self.line_number
- for i in range(1, num_posargs):
- self.write(sep)
- self.preorder(node[i])
- if line_number != self.line_number:
- sep = ",\n" + self.indent + " "
- else:
- sep = ", "
- line_number = self.line_number
-
- i = num_posargs
- j = 0
- # FIXME: adjust output for line breaks?
- while i < n-2:
- self.write(sep)
- self.write(keys[j] + '=')
- self.preorder(node[i])
- if line_number != self.line_number:
- sep = ",\n" + self.indent + " "
- else:
- sep = ", "
- i += 1
- j += 1
- self.write(')')
- self.prune()
- return
- self.n_call_kw36 = n_call_kw36
-
- def starred(node):
- l = len(node)
- assert l > 0
- pos_args = node[0]
- if pos_args == 'expr':
- pos_args = pos_args[0]
- if pos_args == 'tuple':
- build_tuple = pos_args[0]
- if build_tuple.kind.startswith('BUILD_TUPLE'):
- tuple_len = 0
- else:
- tuple_len = len(node) - 1
- star_start = 1
- template = '%C', (0, -1, ', ')
- self.template_engine(template, pos_args)
- if tuple_len == 0:
- self.write("*()")
- # That's it
- self.prune()
- self.write(', ')
- else:
- star_start = 0
- if l > 1:
- template = ( '*%C', (star_start, -1, ', *') )
- else:
- template = ( '*%c', (star_start, 'expr') )
-
- self.template_engine(template, node)
- self.prune()
-
- self.n_starred = starred
-
- def return_closure(node):
- # Nothing should be output here
- self.prune()
- return
- self.n_return_closure = return_closure
-
- if version >= 3.7:
- ########################
- # Python 3.7+ changes
- #######################
-
- PRECEDENCE['attribute37'] = 2
- TABLE_DIRECT.update({
- 'async_forelse_stmt': (
- '%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
- (7, 'store'), (1, 'expr'), (17, 'for_block'), (25, 'else_suite') ),
- 'async_for_stmt': (
- '%|async for %c in %c:\n%+%c%-%-\n\n',
- (7, 'store'), (1, 'expr'), (17, 'for_block')),
- 'async_for_stmt37': (
- '%|async for %c in %c:\n%+%c%-%-\n\n',
- (7, 'store'), (1, 'expr'), (16, 'for_block') ),
- 'attribute37': ( '%c.%[1]{pattr}', 0 ),
- 'compare_chained1a_37': ( ' %[3]{pattr.replace("-", " ")} %p %p',
- (0, 19),
- (-4, 19)),
- 'compare_chained1b_37': ( ' %[3]{pattr.replace("-", " ")} %p %p',
- (0, 19),
- (-4, 19)),
- 'compare_chained2a_37': ( '%[1]{pattr.replace("-", " ")} %p', (0, 19)),
-
- 'compare_chained2b_37': ( '%[1]{pattr.replace("-", " ")} %p', (0, 19)),
-
- })
- if version >= 3.8:
- ########################
- # Python 3.8+ changes
- #######################
-
- # FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest.
- # for lhs in 'for forelsestmt forelselaststmt '
- # 'forelselaststmtl tryfinally38'.split():
- # del TABLE_DIRECT[lhs]
-
- TABLE_DIRECT.update({
- 'async_for_stmt38': (
- '%|async for %c in %c:\n%+%c%-%-\n\n',
- (7, 'store'), (0, 'expr'), (8, 'for_block') ),
-
- 'async_forelse_stmt38': (
- '%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
- (7, 'store'), (0, 'expr'), (8, 'for_block'), (-1, 'else_suite') ),
-
- 'async_with_stmt38': (
- '%|async with %c:\n%+%|%c%-',
- (0, 'expr'), 7),
-
- 'async_with_as_stmt38': (
- '%|async with %c as %c:\n%+%|%c%-',
- (0, 'expr'), (6, 'store'),
- (7, 'suite_stmts') ),
-
- 'except_handler38a': (
- '%c', (-2, 'stmts') ),
- 'except_ret38a': (
- 'return %c', (4, 'expr') ),
- 'except_ret38': ( '%|return %c\n', (1, 'expr') ),
- 'for38': (
- '%|for %c in %c:\n%+%c%-\n\n',
- (2, 'store'),
- (0, 'expr'),
- (3, 'for_block') ),
- 'forelsestmt38': (
- '%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
- (2, 'store'),
- (0, 'expr'),
- (3, 'for_block'), -2 ),
- 'forelselaststmt38': (
- '%|for %c in %c:\n%+%c%-%|else:\n%+%c%-',
- (2, 'store'),
- (0, 'expr'),
- (3, 'for_block'), -2 ),
- 'forelselaststmtl38': (
- '%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
- (2, 'store'),
- (0, 'expr'),
- (3, 'for_block'), -2 ),
-
- 'whilestmt38': ( '%|while %c:\n%+%c%-\n\n',
- (0, 'testexpr'), (1, 'l_stmts') ),
- 'whileTruestmt38': ( '%|while True:\n%+%c%-\n\n',
- (0, 'l_stmts') ),
- 'try_elsestmtl38': (
- '%|try:\n%+%c%-%c%|else:\n%+%c%-',
- (1, 'suite_stmts_opt'),
- (3, 'except_handler38'),
- (5, 'else_suitel') ),
- 'try_except38': (
- '%|try:\n%+%c\n%-%|except:\n%|%-%c\n\n',
- (-2, 'suite_stmts_opt'), (-1, 'except_handler38a') ),
- 'try_except_ret38': (
- '%|try:\n%+%|return %c%-\n%|except:\n%+%|%c%-\n\n',
- (1, 'expr'), (-1, 'except_ret38a') ),
- 'tryfinally38': (
- '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
- (3, 'returns'), 6 ),
- })
- pass # version >= 3.8
- pass
-
- pass # version >= 3.6
- pass # version >= 3.4
+ customize_for_version35(self, version)
+ if version >= 3.6:
+ customize_for_version36(self, version)
+ if version >= 3.7:
+ customize_for_version37(self, version)
+ if version >= 3.8:
+ customize_for_version38(self, version)
+ pass # version >= 3.8
+ pass # 3.7
+ pass # 3.6
+ pass # 3.5
+ pass # 3.4
return
diff --git a/uncompyle6/semantics/customize35.py b/uncompyle6/semantics/customize35.py
new file mode 100644
index 00000000..30a99209
--- /dev/null
+++ b/uncompyle6/semantics/customize35.py
@@ -0,0 +1,230 @@
+# Copyright (c) 2019 by Rocky Bernstein
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""Isolate Python 3.5 version-specific semantic actions here.
+"""
+
+from xdis.code import iscode
+from xdis.util import COMPILER_FLAG_BIT
+from uncompyle6.semantics.consts import (
+ INDENT_PER_LEVEL, TABLE_DIRECT)
+from uncompyle6.semantics.helper import flatten_list
+
+########################
+# Python 3.5+ Additions
+#######################
+def customize_for_version35(self, version):
+ TABLE_DIRECT.update({
+ 'await_expr': ( 'await %c', 0),
+ 'await_stmt': ( '%|%c\n', 0),
+ 'async_for_stmt': (
+ '%|async for %c in %c:\n%+%|%c%-\n\n', 9, 1, 25 ),
+ 'async_forelse_stmt': (
+ '%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
+ 9, 1, 25, (27, 'else_suite') ),
+ 'async_with_stmt': (
+ '%|async with %c:\n%+%|%c%-',
+ (0, 'expr'), 7 ),
+ 'async_with_as_stmt': (
+ '%|async with %c as %c:\n%+%|%c%-',
+ (0, 'expr'), (6, 'store'), 7),
+ 'unmap_dict': ( '{**%C}', (0, -1, ', **') ),
+ # 'unmapexpr': ( '{**%c}', 0), # done by n_unmapexpr
+
+ })
+
+ def async_call(node):
+ self.f.write('async ')
+ node.kind == 'call'
+ p = self.prec
+ self.prec = 80
+ self.template_engine(('%c(%P)', 0, (1, -4, ', ',
+ 100)), node)
+ self.prec = p
+ node.kind == 'async_call'
+ self.prune()
+ self.n_async_call = async_call
+ self.n_build_list_unpack = self.n_list
+
+ def n_call(node):
+ mapping = self._get_mapping(node)
+ table = mapping[0]
+ key = node
+ for i in mapping[1:]:
+ key = key[i]
+ pass
+ if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
+ # Python 3.5 changes the stack position of
+ # *args: kwargs come after *args whereas
+ # in earlier Pythons, *args is at the end
+ # which simplifies things from our
+ # perspective. Python 3.6+ replaces
+ # CALL_FUNCTION_VAR_KW with
+ # CALL_FUNCTION_EX We will just swap the
+ # order to make it look like earlier
+ # Python 3.
+ entry = table[key.kind]
+ kwarg_pos = entry[2][1]
+ args_pos = kwarg_pos - 1
+ # Put last node[args_pos] after subsequent kwargs
+ while node[kwarg_pos] == 'kwarg' and kwarg_pos < len(node):
+ # swap node[args_pos] with node[kwargs_pos]
+ node[kwarg_pos], node[args_pos] = node[args_pos], node[kwarg_pos]
+ args_pos = kwarg_pos
+ kwarg_pos += 1
+ elif key.kind.startswith('CALL_FUNCTION_VAR'):
+ # CALL_FUNCTION_VAR's top element of the stack contains
+ # the variable argument list, then comes
+ # annotation args, then keyword args.
+ # In the most least-top-most stack entry, but position 1
+ # in node order, the positional args.
+ argc = node[-1].attr
+ nargs = argc & 0xFF
+ kwargs = (argc >> 8) & 0xFF
+ # FIXME: handle annotation args
+ if nargs > 0:
+ template = ('%c(%C, ', 0, (1, nargs+1, ', '))
+ else:
+ template = ('%c(', 0)
+ self.template_engine(template, node)
+
+ args_node = node[-2]
+ if args_node in ('pos_arg', 'expr'):
+ args_node = args_node[0]
+ if args_node == 'build_list_unpack':
+ template = ('*%P)', (0, len(args_node)-1, ', *', 100))
+ self.template_engine(template, args_node)
+ else:
+ if len(node) - nargs > 3:
+ template = ('*%c, %C)', nargs+1, (nargs+kwargs+1, -1, ', '))
+ else:
+ template = ('*%c)', nargs+1)
+ self.template_engine(template, node)
+ self.prune()
+
+ self.default(node)
+ self.n_call = n_call
+
+ def n_function_def(node):
+ if self.version >= 3.6:
+ code_node = node[0][0]
+ else:
+ code_node = node[0][1]
+
+ is_code = hasattr(code_node, 'attr') and iscode(code_node.attr)
+ if (is_code and
+ (code_node.attr.co_flags & COMPILER_FLAG_BIT['COROUTINE'])):
+ self.template_engine(('\n\n%|async def %c\n',
+ -2), node)
+ else:
+ self.template_engine(('\n\n%|def %c\n', -2),
+ node)
+ self.prune()
+ self.n_function_def = n_function_def
+
+ def unmapexpr(node):
+ last_n = node[0][-1]
+ for n in node[0]:
+ self.preorder(n)
+ if n != last_n:
+ self.f.write(', **')
+ pass
+ pass
+ self.prune()
+ pass
+ self.n_unmapexpr = unmapexpr
+
+ # FIXME: start here
+ def n_list_unpack(node):
+ """
+ prettyprint an unpacked list or tuple
+ """
+ p = self.prec
+ self.prec = 100
+ lastnode = node.pop()
+ lastnodetype = lastnode.kind
+
+ # If this build list is inside a CALL_FUNCTION_VAR,
+ # then the first * has already been printed.
+ # Until I have a better way to check for CALL_FUNCTION_VAR,
+ # will assume that if the text ends in *.
+ last_was_star = self.f.getvalue().endswith('*')
+
+ if lastnodetype.startswith('BUILD_LIST'):
+ self.write('['); endchar = ']'
+ elif lastnodetype.startswith('BUILD_TUPLE'):
+ # Tuples can appear places that can NOT
+ # have parenthesis around them, like array
+ # subscripts. We check for that by seeing
+ # if a tuple item is some sort of slice.
+ no_parens = False
+ for n in node:
+ if n == 'expr' and n[0].kind.startswith('build_slice'):
+ no_parens = True
+ break
+ pass
+ if no_parens:
+ endchar = ''
+ else:
+ self.write('('); endchar = ')'
+ pass
+
+ elif lastnodetype.startswith('BUILD_SET'):
+ self.write('{'); endchar = '}'
+ elif lastnodetype.startswith('BUILD_MAP_UNPACK'):
+ self.write('{*'); endchar = '}'
+ elif lastnodetype.startswith('ROT_TWO'):
+ self.write('('); endchar = ')'
+ else:
+ raise TypeError('Internal Error: n_build_list expects list, tuple, set, or unpack')
+
+ flat_elems = flatten_list(node)
+
+ self.indent_more(INDENT_PER_LEVEL)
+ sep = ''
+ for elem in flat_elems:
+ if elem in ('ROT_THREE', 'EXTENDED_ARG'):
+ continue
+ assert elem == 'expr'
+ line_number = self.line_number
+ value = self.traverse(elem)
+ if elem[0] == 'tuple':
+ assert value[0] == '('
+ assert value[-1] == ')'
+ value = value[1:-1]
+ if value[-1] == ',':
+ # singleton tuple
+ value = value[:-1]
+ else:
+ value = '*' + value
+ if line_number != self.line_number:
+ sep += '\n' + self.indent + INDENT_PER_LEVEL[:-1]
+ else:
+ if sep != '': sep += ' '
+ if not last_was_star:
+ pass
+ else:
+ last_was_star = False
+ self.write(sep, value)
+ sep = ','
+ if lastnode.attr == 1 and lastnodetype.startswith('BUILD_TUPLE'):
+ self.write(',')
+ self.write(endchar)
+ self.indent_less(INDENT_PER_LEVEL)
+
+ self.prec = p
+ self.prune()
+ return
+
+ self.n_tuple_unpack = n_list_unpack
diff --git a/uncompyle6/semantics/customize36.py b/uncompyle6/semantics/customize36.py
new file mode 100644
index 00000000..6c2a547b
--- /dev/null
+++ b/uncompyle6/semantics/customize36.py
@@ -0,0 +1,448 @@
+# Copyright (c) 2019 by Rocky Bernstein
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""Isolate Python 3.6 version-specific semantic actions here.
+"""
+
+from spark_parser.ast import GenericASTTraversalPruningException
+from uncompyle6.scanners.tok import Token
+from uncompyle6.semantics.helper import flatten_list
+from uncompyle6.semantics.consts import (
+ INDENT_PER_LEVEL, PRECEDENCE, TABLE_DIRECT, TABLE_R)
+
+#######################
+# Python 3.6+ Changes #
+#######################
+
+def customize_for_version36(self, version):
+ # Value 100 is important; it is exactly
+ # module/function precidence.
+ PRECEDENCE['call_kw'] = 100
+ PRECEDENCE['call_kw36'] = 100
+ PRECEDENCE['call_ex'] = 100
+ PRECEDENCE['call_ex_kw'] = 100
+ PRECEDENCE['call_ex_kw2'] = 100
+ PRECEDENCE['call_ex_kw3'] = 100
+ PRECEDENCE['call_ex_kw4'] = 100
+ PRECEDENCE['unmap_dict'] = 0
+
+ TABLE_DIRECT.update({
+ 'tryfinally36': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
+ (1, 'returns'), 3 ),
+ 'fstring_expr': ( "{%c%{conversion}}", 0),
+ # FIXME: the below assumes the format strings
+ # don't have ''' in them. Fix this properly
+ 'fstring_single': ( "f'''{%c%{conversion}}'''", 0),
+ 'fstring_multi': ( "f'''%c'''", 0),
+ 'func_args36': ( "%c(**", 0),
+ 'try_except36': ( '%|try:\n%+%c%-%c\n\n', 1, 2 ),
+ 'except_return': ( '%|except:\n%+%c%-', 3 ),
+ 'unpack_list': ( '*%c', (0, 'list') ),
+ 'tryfinally_return_stmt':
+ ( '%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n', 1 ),
+
+ 'async_for_stmt36': (
+ '%|async for %c in %c:\n%+%c%-%-\n\n',
+ (9, 'store'), (1, 'expr'), (18, 'for_block') ),
+
+ 'call_ex' : (
+ '%c(%p)',
+ (0, 'expr'), (1, 100)),
+ 'call_ex_kw' : (
+ '%c(%p)',
+ (0, 'expr'), (2, 100)),
+
+ })
+
+ TABLE_R.update({
+ 'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)),
+ # Not quite right
+ 'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2, 3, ',')),
+ })
+
+ def build_unpack_tuple_with_call(node):
+
+ if node[0] == 'expr':
+ tup = node[0][0]
+ else:
+ tup = node[0]
+ pass
+ assert tup == 'tuple'
+ self.call36_tuple(tup)
+
+ buwc = node[-1]
+ assert buwc.kind.startswith('BUILD_TUPLE_UNPACK_WITH_CALL')
+ for n in node[1:-1]:
+ self.f.write(', *')
+ self.preorder(n)
+ pass
+ self.prune()
+ return
+ self.n_build_tuple_unpack_with_call = build_unpack_tuple_with_call
+
+ def build_unpack_map_with_call(node):
+ n = node[0]
+ if n == 'expr':
+ n = n[0]
+ if n == 'dict':
+ self.call36_dict(n)
+ first = 1
+ sep = ', **'
+ else:
+ first = 0
+ sep = '**'
+ for n in node[first:-1]:
+ self.f.write(sep)
+ self.preorder(n)
+ sep = ', **'
+ pass
+ self.prune()
+ return
+ self.n_build_map_unpack_with_call = build_unpack_map_with_call
+
+ def call_ex_kw2(node):
+ """Handle CALL_FUNCTION_EX 2 (have KW) but with
+ BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""
+
+ # This is weird shit. Thanks Python!
+ self.preorder(node[0])
+ self.write('(')
+
+ assert node[1] == 'build_tuple_unpack_with_call'
+ btuwc = node[1]
+ tup = btuwc[0]
+ if tup == 'expr':
+ tup = tup[0]
+ assert tup == 'tuple'
+ self.call36_tuple(tup)
+ assert node[2] == 'build_map_unpack_with_call'
+
+ self.write(', ')
+ d = node[2][0]
+ if d == 'expr':
+ d = d[0]
+ assert d == 'dict'
+ self.call36_dict(d)
+
+ args = btuwc[1]
+ self.write(', *')
+ self.preorder(args)
+
+ self.write(', **')
+ star_star_args = node[2][1]
+ if star_star_args == 'expr':
+ star_star_args = star_star_args[0]
+ self.preorder(star_star_args)
+ self.write(')')
+ self.prune()
+ self.n_call_ex_kw2 = call_ex_kw2
+
+ def call_ex_kw3(node):
+ """Handle CALL_FUNCTION_EX 1 (have KW) but without
+ BUILD_MAP_UNPACK_WITH_CALL"""
+ self.preorder(node[0])
+ self.write('(')
+ args = node[1][0]
+ if args == 'expr':
+ args = args[0]
+ if args == 'tuple':
+ if self.call36_tuple(args) > 0:
+ self.write(', ')
+ pass
+ pass
+
+ self.write('*')
+ self.preorder(node[1][1])
+ self.write(', ')
+
+ kwargs = node[2]
+ if kwargs == 'expr':
+ kwargs = kwargs[0]
+ if kwargs == 'dict':
+ self.call36_dict(kwargs)
+ else:
+ self.write('**')
+ self.preorder(kwargs)
+ self.write(')')
+ self.prune()
+ self.n_call_ex_kw3 = call_ex_kw3
+
+ def call_ex_kw4(node):
+ """Handle CALL_FUNCTION_EX {1 or 2} but without
+ BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""
+ self.preorder(node[0])
+ self.write('(')
+ args = node[1][0]
+ if args == 'tuple':
+ if self.call36_tuple(args) > 0:
+ self.write(', ')
+ pass
+ pass
+ else:
+ self.write('*')
+ self.preorder(args)
+ self.write(', ')
+ pass
+
+ kwargs = node[2]
+ if kwargs == 'expr':
+ kwargs = kwargs[0]
+ call_function_ex = node[-1]
+ assert (call_function_ex == 'CALL_FUNCTION_EX_KW'
+ or (self.version >= 3.6 and call_function_ex == 'CALL_FUNCTION_EX'))
+ # FIXME: decide if the below test be on kwargs == 'dict'
+ if (call_function_ex.attr & 1 and
+ (not isinstance(kwargs, Token) and kwargs != 'attribute')
+ and not kwargs[0].kind.startswith('kvlist')):
+ self.call36_dict(kwargs)
+ else:
+ self.write('**')
+ self.preorder(kwargs)
+ self.write(')')
+ self.prune()
+ self.n_call_ex_kw4 = call_ex_kw4
+
+ def call36_tuple(node):
+ """
+ A tuple used in a call, these are like normal tuples but they
+ don't have the enclosing parenthesis.
+ """
+ assert node == 'tuple'
+ # Note: don't iterate over last element which is a
+ # BUILD_TUPLE...
+ flat_elems = flatten_list(node[:-1])
+
+ self.indent_more(INDENT_PER_LEVEL)
+ sep = ''
+
+ for elem in flat_elems:
+ if elem in ('ROT_THREE', 'EXTENDED_ARG'):
+ continue
+ assert elem == 'expr'
+ line_number = self.line_number
+ value = self.traverse(elem)
+ if line_number != self.line_number:
+ sep += '\n' + self.indent + INDENT_PER_LEVEL[:-1]
+ self.write(sep, value)
+ sep = ', '
+
+ self.indent_less(INDENT_PER_LEVEL)
+ return len(flat_elems)
+ self.call36_tuple = call36_tuple
+
+ def call36_dict(node):
+ """
+ A dict used in a call_ex_kw2, which are a dictionary items expressed
+ in a call. This should format to:
+ a=1, b=2
+ In other words, no braces, no quotes around keys and ":" becomes
+ "=".
+
+ We will source-code use line breaks to guide us when to break.
+ """
+ p = self.prec
+ self.prec = 100
+
+ self.indent_more(INDENT_PER_LEVEL)
+ sep = INDENT_PER_LEVEL[:-1]
+ line_number = self.line_number
+
+ if node[0].kind.startswith('kvlist'):
+ # Python 3.5+ style key/value list in dict
+ kv_node = node[0]
+ l = list(kv_node)
+ i = 0
+
+ length = len(l)
+ # FIXME: Parser-speed improved grammars will have BUILD_MAP
+ # at the end. So in the future when everything is
+ # complete, we can do an "assert" instead of "if".
+ if kv_node[-1].kind.startswith("BUILD_MAP"):
+ length -= 1
+
+ # Respect line breaks from source
+ while i < length:
+ self.write(sep)
+ name = self.traverse(l[i], indent='')
+ # Strip off beginning and trailing quotes in name
+ name = name[1:-1]
+ if i > 0:
+ line_number = self.indent_if_source_nl(line_number,
+ self.indent + INDENT_PER_LEVEL[:-1])
+ line_number = self.line_number
+ self.write(name, '=')
+ value = self.traverse(l[i+1], indent=self.indent+(len(name)+2)*' ')
+ self.write(value)
+ sep = ", "
+ if line_number != self.line_number:
+ sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
+ line_number = self.line_number
+ i += 2
+ pass
+ elif node[-1].kind.startswith('BUILD_CONST_KEY_MAP'):
+ keys_node = node[-2]
+ keys = keys_node.attr
+ # from trepan.api import debug; debug()
+ assert keys_node == 'LOAD_CONST' and isinstance(keys, tuple)
+ for i in range(node[-1].attr):
+ self.write(sep)
+ self.write(keys[i], '=')
+ value = self.traverse(node[i], indent='')
+ self.write(value)
+ sep = ", "
+ if line_number != self.line_number:
+ sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
+ line_number = self.line_number
+ pass
+ pass
+ else:
+ self.write("**")
+ try:
+ self.default(node)
+ except GenericASTTraversalPruningException:
+ pass
+
+ self.prec = p
+ self.indent_less(INDENT_PER_LEVEL)
+ return
+ self.call36_dict = call36_dict
+
+
+ FSTRING_CONVERSION_MAP = {1: '!s', 2: '!r', 3: '!a'}
+
+ def n_except_suite_finalize(node):
+ if node[1] == 'returns' and self.hide_internal:
+ # Process node[1] only.
+ # The code after "returns", e.g. node[3], is dead code.
+ # Adding it is wrong as it dedents and another
+ # exception handler "except_stmt" afterwards.
+ # Note it is also possible that the grammar is wrong here.
+ # and this should not be "except_stmt".
+ self.indent_more()
+ self.preorder(node[1])
+ self.indent_less()
+ else:
+ self.default(node)
+ self.prune()
+ self.n_except_suite_finalize = n_except_suite_finalize
+
+ def n_formatted_value(node):
+ if node[0] == 'LOAD_CONST':
+ self.write(node[0].attr)
+ self.prune()
+ else:
+ self.default(node)
+ self.n_formatted_value = n_formatted_value
+
+ def f_conversion(node):
+ node.conversion = FSTRING_CONVERSION_MAP.get(node.data[1].attr, '')
+
+ def fstring_expr(node):
+ f_conversion(node)
+ self.default(node)
+ self.n_fstring_expr = fstring_expr
+
+ def fstring_single(node):
+ f_conversion(node)
+ self.default(node)
+ self.n_fstring_single = fstring_single
+
+ # def kwargs_only_36(node):
+ # keys = node[-1].attr
+ # num_kwargs = len(keys)
+ # values = node[:num_kwargs]
+ # for i, (key, value) in enumerate(zip(keys, values)):
+ # self.write(key + '=')
+ # self.preorder(value)
+ # if i < num_kwargs:
+ # self.write(',')
+ # self.prune()
+ # return
+ # self.n_kwargs_only_36 = kwargs_only_36
+
+ def n_call_kw36(node):
+ self.template_engine(("%c(", 0), node)
+ keys = node[-2].attr
+ num_kwargs = len(keys)
+ num_posargs = len(node) - (num_kwargs + 2)
+ n = len(node)
+ assert n >= len(keys)+1, \
+ 'not enough parameters keyword-tuple values'
+ sep = ''
+
+ line_number = self.line_number
+ for i in range(1, num_posargs):
+ self.write(sep)
+ self.preorder(node[i])
+ if line_number != self.line_number:
+ sep = ",\n" + self.indent + " "
+ else:
+ sep = ", "
+ line_number = self.line_number
+
+ i = num_posargs
+ j = 0
+ # FIXME: adjust output for line breaks?
+ while i < n-2:
+ self.write(sep)
+ self.write(keys[j] + '=')
+ self.preorder(node[i])
+ if line_number != self.line_number:
+ sep = ",\n" + self.indent + " "
+ else:
+ sep = ", "
+ i += 1
+ j += 1
+ self.write(')')
+ self.prune()
+ return
+ self.n_call_kw36 = n_call_kw36
+
+ def starred(node):
+ l = len(node)
+ assert l > 0
+ pos_args = node[0]
+ if pos_args == 'expr':
+ pos_args = pos_args[0]
+ if pos_args == 'tuple':
+ build_tuple = pos_args[0]
+ if build_tuple.kind.startswith('BUILD_TUPLE'):
+ tuple_len = 0
+ else:
+ tuple_len = len(node) - 1
+ star_start = 1
+ template = '%C', (0, -1, ', ')
+ self.template_engine(template, pos_args)
+ if tuple_len == 0:
+ self.write("*()")
+ # That's it
+ self.prune()
+ self.write(', ')
+ else:
+ star_start = 0
+ if l > 1:
+ template = ( '*%C', (star_start, -1, ', *') )
+ else:
+ template = ( '*%c', (star_start, 'expr') )
+
+ self.template_engine(template, node)
+ self.prune()
+
+ self.n_starred = starred
+
+ def return_closure(node):
+ # Nothing should be output here
+ self.prune()
+ return
+ self.n_return_closure = return_closure
diff --git a/uncompyle6/semantics/customize37.py b/uncompyle6/semantics/customize37.py
new file mode 100644
index 00000000..c5fb32a1
--- /dev/null
+++ b/uncompyle6/semantics/customize37.py
@@ -0,0 +1,47 @@
+# Copyright (c) 2019 by Rocky Bernstein
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""Isolate Python 3.7 version-specific semantic actions here.
+"""
+
+from uncompyle6.semantics.consts import PRECEDENCE, TABLE_DIRECT
+
+def customize_for_version37(self, version):
+ ########################
+ # Python 3.7+ changes
+ #######################
+
+ PRECEDENCE['attribute37'] = 2
+ TABLE_DIRECT.update({
+ 'async_forelse_stmt': (
+ '%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
+ (7, 'store'), (1, 'expr'), (17, 'for_block'), (25, 'else_suite') ),
+ 'async_for_stmt': (
+ '%|async for %c in %c:\n%+%c%-%-\n\n',
+ (7, 'store'), (1, 'expr'), (17, 'for_block')),
+ 'async_for_stmt37': (
+ '%|async for %c in %c:\n%+%c%-%-\n\n',
+ (7, 'store'), (1, 'expr'), (16, 'for_block') ),
+ 'attribute37': ( '%c.%[1]{pattr}', 0 ),
+ 'compare_chained1a_37': ( ' %[3]{pattr.replace("-", " ")} %p %p',
+ (0, 19),
+ (-4, 19)),
+ 'compare_chained1b_37': ( ' %[3]{pattr.replace("-", " ")} %p %p',
+ (0, 19),
+ (-4, 19)),
+ 'compare_chained2a_37': ( '%[1]{pattr.replace("-", " ")} %p', (0, 19)),
+
+ 'compare_chained2b_37': ( '%[1]{pattr.replace("-", " ")} %p', (0, 19)),
+
+ })
diff --git a/uncompyle6/semantics/customize38.py b/uncompyle6/semantics/customize38.py
new file mode 100644
index 00000000..ebea328c
--- /dev/null
+++ b/uncompyle6/semantics/customize38.py
@@ -0,0 +1,93 @@
+# Copyright (c) 2019 by Rocky Bernstein
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""Isolate Python 3.6 version-specific semantic actions here.
+"""
+
+########################
+# Python 3.8+ changes
+#######################
+
+from uncompyle6.semantics.consts import TABLE_DIRECT
+
+def customize_for_version38(self, version):
+
+ # FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest.
+ # for lhs in 'for forelsestmt forelselaststmt '
+ # 'forelselaststmtl tryfinally38'.split():
+ # del TABLE_DIRECT[lhs]
+
+ TABLE_DIRECT.update({
+ 'async_for_stmt38': (
+ '%|async for %c in %c:\n%+%c%-%-\n\n',
+ (7, 'store'), (0, 'expr'), (8, 'for_block') ),
+
+ 'async_forelse_stmt38': (
+ '%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
+ (7, 'store'), (0, 'expr'), (8, 'for_block'), (-1, 'else_suite') ),
+
+ 'async_with_stmt38': (
+ '%|async with %c:\n%+%|%c%-',
+ (0, 'expr'), 7),
+
+ 'async_with_as_stmt38': (
+ '%|async with %c as %c:\n%+%|%c%-',
+ (0, 'expr'), (6, 'store'),
+ (7, 'suite_stmts') ),
+
+ 'except_handler38a': (
+ '%c', (-2, 'stmts') ),
+ 'except_ret38a': (
+ 'return %c', (4, 'expr') ),
+ 'except_ret38': ( '%|return %c\n', (1, 'expr') ),
+ 'for38': (
+ '%|for %c in %c:\n%+%c%-\n\n',
+ (2, 'store'),
+ (0, 'expr'),
+ (3, 'for_block') ),
+ 'forelsestmt38': (
+ '%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
+ (2, 'store'),
+ (0, 'expr'),
+ (3, 'for_block'), -2 ),
+ 'forelselaststmt38': (
+ '%|for %c in %c:\n%+%c%-%|else:\n%+%c%-',
+ (2, 'store'),
+ (0, 'expr'),
+ (3, 'for_block'), -2 ),
+ 'forelselaststmtl38': (
+ '%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
+ (2, 'store'),
+ (0, 'expr'),
+ (3, 'for_block'), -2 ),
+
+ 'whilestmt38': ( '%|while %c:\n%+%c%-\n\n',
+ (0, 'testexpr'), (1, 'l_stmts') ),
+ 'whileTruestmt38': ( '%|while True:\n%+%c%-\n\n',
+ (0, 'l_stmts') ),
+ 'try_elsestmtl38': (
+ '%|try:\n%+%c%-%c%|else:\n%+%c%-',
+ (1, 'suite_stmts_opt'),
+ (3, 'except_handler38'),
+ (5, 'else_suitel') ),
+ 'try_except38': (
+ '%|try:\n%+%c\n%-%|except:\n%|%-%c\n\n',
+ (-2, 'suite_stmts_opt'), (-1, 'except_handler38a') ),
+ 'try_except_ret38': (
+ '%|try:\n%+%|return %c%-\n%|except:\n%+%|%c%-\n\n',
+ (1, 'expr'), (-1, 'except_ret38a') ),
+ 'tryfinally38': (
+ '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
+ (3, 'returns'), 6 ),
+ })