diff --git a/pytest/test_grammar.py b/pytest/test_grammar.py index 73cda0ce..fd2771c9 100644 --- a/pytest/test_grammar.py +++ b/pytest/test_grammar.py @@ -88,7 +88,7 @@ def test_grammar(): COME_FROM_EXCEPT_CLAUSE COME_FROM_LOOP COME_FROM_WITH COME_FROM_FINALLY ELSE - LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP + LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_STR LAMBDA_MARKER RETURN_END_IF RETURN_END_IF_LAMBDA RETURN_VALUE_LAMBDA RETURN_LAST """.split()) diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index 24eac0ab..757c19c5 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -112,10 +112,9 @@ class Python3Parser(PythonParser): continues ::= continue - kwarg ::= LOAD_CONST expr + kwarg ::= LOAD_STR expr kwargs ::= kwarg+ - classdef ::= build_class store # FIXME: we need to add these because don't detect this properly @@ -394,11 +393,12 @@ class Python3Parser(PythonParser): def p_generator_exp3(self, args): ''' load_genexpr ::= LOAD_GENEXPR - load_genexpr ::= BUILD_TUPLE_1 LOAD_GENEXPR LOAD_CONST + load_genexpr ::= BUILD_TUPLE_1 LOAD_GENEXPR LOAD_STR ''' def p_expr3(self, args): """ + expr ::= LOAD_STR expr ::= conditionalnot conditionalnot ::= expr jmp_true expr jump_forward_else expr COME_FROM @@ -441,7 +441,7 @@ class Python3Parser(PythonParser): break pass assert i < len(tokens), "build_class needs to find MAKE_FUNCTION or MAKE_CLOSURE" - assert tokens[i+1].kind == 'LOAD_CONST', \ + assert tokens[i+1].kind == 'LOAD_STR', \ "build_class expecting CONST after MAKE_FUNCTION/MAKE_CLOSURE" call_fn_tok = None for i in range(i, len(tokens)): @@ -515,13 +515,13 @@ class Python3Parser(PythonParser): self.add_unique_rule(rule, token.kind, uniq_param, customize) def add_make_function_rule(self, rule, opname, attr, customize): - """Python 3.3 added a an addtional LOAD_CONST before MAKE_FUNCTION and + """Python 3.3 added a an addtional LOAD_STR before MAKE_FUNCTION and this has an effect on many rules. """ if self.version >= 3.3: - new_rule = rule % (('LOAD_CONST ') * 1) + new_rule = rule % (('LOAD_STR ') * 1) else: - new_rule = rule % (('LOAD_CONST ') * 0) + new_rule = rule % (('LOAD_STR ') * 0) self.add_unique_rule(new_rule, opname, attr, customize) def customize_grammar_rules(self, tokens, customize): @@ -730,7 +730,7 @@ class Python3Parser(PythonParser): if opname == 'CALL_FUNCTION' and token.attr == 1: rule = """ - dict_comp ::= LOAD_DICTCOMP LOAD_CONST MAKE_FUNCTION_0 expr + dict_comp ::= LOAD_DICTCOMP LOAD_STR MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1 classdefdeco1 ::= expr classdefdeco2 CALL_FUNCTION_1 """ @@ -849,7 +849,7 @@ class Python3Parser(PythonParser): # Note that 3.6+ doesn't do this, but we'll remove # this rule in parse36.py rule = """ - dict_comp ::= load_closure LOAD_DICTCOMP LOAD_CONST + dict_comp ::= load_closure LOAD_DICTCOMP LOAD_STR MAKE_CLOSURE_0 expr GET_ITER CALL_FUNCTION_1 """ @@ -902,10 +902,10 @@ class Python3Parser(PythonParser): rule = ('mkfunc ::= %s%sload_closure LOAD_CONST %s' % (kwargs_str, 'expr ' * args_pos, opname)) elif self.version == 3.3: - rule = ('mkfunc ::= %s%sload_closure LOAD_CONST LOAD_CONST %s' + rule = ('mkfunc ::= %s%sload_closure LOAD_CONST LOAD_STR %s' % (kwargs_str, 'expr ' * args_pos, opname)) elif self.version >= 3.4: - rule = ('mkfunc ::= %s%s load_closure LOAD_CONST LOAD_CONST %s' + rule = ('mkfunc ::= %s%s load_closure LOAD_CONST LOAD_STR %s' % ('expr ' * args_pos, kwargs_str, opname)) self.add_unique_rule(rule, opname, token.attr, customize) @@ -933,17 +933,17 @@ class Python3Parser(PythonParser): rule = ('mklambda ::= %s%s%s%s' % ('expr ' * stack_count, 'load_closure ' * closure, - 'BUILD_TUPLE_1 LOAD_LAMBDA LOAD_CONST ', + 'BUILD_TUPLE_1 LOAD_LAMBDA LOAD_STR ', opname)) else: rule = ('mklambda ::= %s%s%s' % ('load_closure ' * closure, - 'LOAD_LAMBDA LOAD_CONST ', + 'LOAD_LAMBDA LOAD_STR ', opname)) self.add_unique_rule(rule, opname, token.attr, customize) else: - rule = ('mklambda ::= %sLOAD_LAMBDA LOAD_CONST %s' % + rule = ('mklambda ::= %sLOAD_LAMBDA LOAD_STR %s' % (('expr ' * stack_count), opname)) self.add_unique_rule(rule, opname, token.attr, customize) @@ -951,7 +951,7 @@ class Python3Parser(PythonParser): rule = ('mkfunc ::= %s%s%s%s' % ('expr ' * stack_count, 'load_closure ' * closure, - 'LOAD_CONST ' * 2, + 'LOAD_CONST LOAD_STR ', opname)) self.add_unique_rule(rule, opname, token.attr, customize) @@ -1033,17 +1033,17 @@ class Python3Parser(PythonParser): elif self.version == 3.3: # positional args after keyword args rule = ('mkfunc ::= %s %s%s%s' % - (kwargs, 'pos_arg ' * args_pos, 'LOAD_CONST '*2, + (kwargs, 'pos_arg ' * args_pos, 'LOAD_CONST LOAD_STR ', opname)) elif self.version > 3.5: # positional args before keyword args rule = ('mkfunc ::= %s%s %s%s' % - ('pos_arg ' * args_pos, kwargs, 'LOAD_CONST '*2, + ('pos_arg ' * args_pos, kwargs, 'LOAD_CONST LOAD_STR ', opname)) elif self.version > 3.3: # positional args before keyword args rule = ('mkfunc ::= %s%s %s%s' % - ('pos_arg ' * args_pos, kwargs, 'LOAD_CONST '*2, + ('pos_arg ' * args_pos, kwargs, 'LOAD_CONST LOAD_STR ', opname)) else: rule = ('mkfunc ::= %s%sexpr %s' % @@ -1052,22 +1052,22 @@ class Python3Parser(PythonParser): if opname.startswith('MAKE_FUNCTION_A'): if self.version >= 3.6: - rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST %s' % + rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_STR %s' % (('pos_arg ' * (args_pos)), ('call ' * (annotate_args-1)), opname)) self.add_unique_rule(rule, opname, token.attr, customize) - rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST %s' % + rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_STR %s' % (('pos_arg ' * (args_pos)), ('annotate_arg ' * (annotate_args-1)), opname)) if self.version >= 3.3: # Normally we remove EXTENDED_ARG from the opcodes, but in the case of # annotated functions can use the EXTENDED_ARG tuple to signal we have an annotated function. # Yes this is a little hacky - rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST EXTENDED_ARG %s' % + rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_STR EXTENDED_ARG %s' % (('pos_arg ' * (args_pos)), ('call ' * (annotate_args-1)), opname)) self.add_unique_rule(rule, opname, token.attr, customize) - rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST EXTENDED_ARG %s' % + rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_STR EXTENDED_ARG %s' % (('pos_arg ' * (args_pos)), ('annotate_arg ' * (annotate_args-1)), opname)) else: @@ -1143,7 +1143,8 @@ class Python3Parser(PythonParser): self.check_reduce['while1elsestmt'] = 'noAST' self.check_reduce['ifelsestmt'] = 'AST' self.check_reduce['annotate_tuple'] = 'noAST' - self.check_reduce['kwarg'] = 'noAST' + if not PYTHON3: + self.check_reduce['kwarg'] = 'noAST' if self.version < 3.6: # 3.6+ can remove a JUMP_FORWARD which messes up our testing here self.check_reduce['try_except'] = 'AST' @@ -1160,10 +1161,7 @@ class Python3Parser(PythonParser): return not isinstance(tokens[first].attr, tuple) elif lhs == 'kwarg': arg = tokens[first].attr - if PYTHON3: - return not isinstance(arg, str) - else: - return not (isinstance(arg, str) or isinstance(arg, unicode)) + return not (isinstance(arg, str) or isinstance(arg, unicode)) elif lhs == 'while1elsestmt': n = len(tokens) diff --git a/uncompyle6/parsers/parse36.py b/uncompyle6/parsers/parse36.py index f87da84b..abb80cf4 100644 --- a/uncompyle6/parsers/parse36.py +++ b/uncompyle6/parsers/parse36.py @@ -202,14 +202,14 @@ class Python36Parser(Python35Parser): if 'LOAD_DICTCOMP' in self.seen_ops: # Is there something general going on here? rule = """ - dict_comp ::= load_closure LOAD_DICTCOMP LOAD_CONST + dict_comp ::= load_closure LOAD_DICTCOMP LOAD_STR MAKE_FUNCTION_8 expr GET_ITER CALL_FUNCTION_1 """ self.addRule(rule, nop_func) elif 'LOAD_SETCOMP' in self.seen_ops: rule = """ - set_comp ::= load_closure LOAD_SETCOMP LOAD_CONST + set_comp ::= load_closure LOAD_SETCOMP LOAD_STR MAKE_FUNCTION_8 expr GET_ITER CALL_FUNCTION_1 """ diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index 9eeabc7c..491ac49d 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -310,6 +310,8 @@ class Scanner3(Scanner): # pattr = 'code_object @ 0x%x %s->%s' %\ # (id(const), const.co_filename, const.co_name) pattr = '' + elif isinstance(const, str): + opname = 'LOAD_STR' else: if isinstance(inst.arg, int) and inst.arg < len(co.co_consts): argval, _ = _get_const_info(inst.arg, co.co_consts) diff --git a/uncompyle6/semantics/consts.py b/uncompyle6/semantics/consts.py index 10707eea..3585920f 100644 --- a/uncompyle6/semantics/consts.py +++ b/uncompyle6/semantics/consts.py @@ -131,7 +131,7 @@ PASS = SyntaxTree('stmts', ASSIGN_DOC_STRING = lambda doc_string: \ SyntaxTree('stmt', [ SyntaxTree('assign', - [ SyntaxTree('expr', [ Token('LOAD_CONST', pattr=doc_string) ]), + [ SyntaxTree('expr', [ Token('LOAD_STR', pattr=doc_string) ]), SyntaxTree('store', [ Token('STORE_NAME', pattr='__doc__')]) ])]) @@ -221,8 +221,9 @@ TABLE_DIRECT = { 'IMPORT_FROM': ( '%{pattr}', ), 'attribute': ( '%c.%[1]{pattr}', (0, 'expr')), - 'LOAD_FAST': ( '%{pattr}', ), - 'LOAD_NAME': ( '%{pattr}', ), + 'LOAD_STR': ( '%{pattr}', ), + 'LOAD_FAST': ( '%{pattr}', ), + 'LOAD_NAME': ( '%{pattr}', ), 'LOAD_CLASSNAME': ( '%{pattr}', ), 'LOAD_GLOBAL': ( '%{pattr}', ), 'LOAD_DEREF': ( '%{pattr}', ), @@ -317,7 +318,7 @@ TABLE_DIRECT = { 'mkfuncdeco0': ( '%|def %c\n', 0), 'classdefdeco': ( '\n\n%c', 0), 'classdefdeco1': ( '%|@%c\n%c', 0, 1), - 'kwarg': ( '%[0]{pattr}=%c', 1), + 'kwarg': ( '%[0]{attr}=%c', 1), 'kwargs': ( '%D', (0, maxint, ', ') ), 'kwargs1': ( '%D', (0, maxint, ', ') ), diff --git a/uncompyle6/semantics/customize3.py b/uncompyle6/semantics/customize3.py index 83a69bb9..7eabfd58 100644 --- a/uncompyle6/semantics/customize3.py +++ b/uncompyle6/semantics/customize3.py @@ -62,11 +62,11 @@ def customize_for_version3(self, version): subclass_info = None if node == 'classdefdeco2': if self.version >= 3.6: - class_name = node[1][1].pattr + class_name = node[1][1].attr elif self.version <= 3.3: - class_name = node[2][0].pattr + class_name = node[2][0].attr else: - class_name = node[1][2].pattr + class_name = node[1][2].attr build_class = node else: build_class = node[0] diff --git a/uncompyle6/semantics/customize36.py b/uncompyle6/semantics/customize36.py index 2245e648..97191f90 100644 --- a/uncompyle6/semantics/customize36.py +++ b/uncompyle6/semantics/customize36.py @@ -77,7 +77,7 @@ def customize_for_version36(self, version): self.call36_tuple(n) first = 1 sep = ', *' - elif n == 'LOAD_CONST': + elif n == 'LOAD_STR': value = self.format_pos_args(n) self.f.write(value) first = 1 @@ -401,7 +401,7 @@ def customize_for_version36(self, version): self.n_except_suite_finalize = n_except_suite_finalize def n_formatted_value(node): - if node[0] == 'LOAD_CONST': + if node[0] in ('LOAD_STR', 'LOAD_CONST'): value = node[0].attr if isinstance(value, tuple): self.write(node[0].attr) @@ -415,7 +415,7 @@ def customize_for_version36(self, version): def n_formatted_value_attr(node): f_conversion(node) fmt_node = node.data[3] - if fmt_node == 'expr' and fmt_node[0] == 'LOAD_CONST': + if fmt_node == 'expr' and fmt_node[0] == 'LOAD_STR': node.string = escape_format(fmt_node[0].attr) else: node.string = fmt_node @@ -424,7 +424,7 @@ def customize_for_version36(self, version): def f_conversion(node): fmt_node = node.data[1] - if fmt_node == 'expr' and fmt_node[0] == 'LOAD_CONST': + if fmt_node == 'expr' and fmt_node[0] == 'LOAD_STR': data = fmt_node[0].attr else: data = fmt_node.attr @@ -482,11 +482,11 @@ def customize_for_version36(self, version): else: # {{ and }} in Python source-code format strings mean # { and } respectively. But only when *not* part of a - # formatted value. However in the LOAD_CONST + # formatted value. However in the LOAD_STR # bytecode, the escaping of the braces has been # removed. So we need to put back the braces escaping in # reconstructing the source. - assert expr[0] == 'LOAD_CONST' + assert expr[0] == 'LOAD_STR' value = value.replace("{", "{{").replace("}", "}}") # Remove leading quotes diff --git a/uncompyle6/semantics/fragments.py b/uncompyle6/semantics/fragments.py index 968a69cc..e385d347 100644 --- a/uncompyle6/semantics/fragments.py +++ b/uncompyle6/semantics/fragments.py @@ -421,6 +421,7 @@ class FragmentsWalker(pysource.SourceWalker, object): pass self.set_pos_info(node, start, len(self.f.getvalue())) self.prune() + n_LOAD_STR = n_LOAD_CONST def n_exec_stmt(self, node): """