diff --git a/test/Makefile b/test/Makefile index ed4b2f5a..233b4858 100644 --- a/test/Makefile +++ b/test/Makefile @@ -114,8 +114,14 @@ grammar-coverage-2.6: #: Get grammar coverage for Python 2.7 grammar-coverage-2.7: - SPARK_PARSER_COVERAGE=/tmp/spark-grammar-27.cover $(PYTHON) test_pythonlib.py --bytecode-2.7 - SPARK_PARSER_COVERAGE=/tmp/spark-grammar-27.cover $(PYTHON) test_pyenvlib.py --2.7.13 + SPARK_PARSER_COVERAGE=$(COVER_DIR)/spark-grammar-27.cover $(PYTHON) test_pythonlib.py --bytecode-2.7 + SPARK_PARSER_COVERAGE=$(COVER_DIR)/spark-grammar-27.cover $(PYTHON) test_pyenvlib.py --2.7.13 + +#: Get grammar coverage for Python 3.2 +grammar-coverage-3.2: + rm $(COVER_DIR)/spark-grammar-32.cover || /bin/true + SPARK_PARSER_COVERAGE=$(COVER_DIR)/spark-grammar-32.cover $(PYTHON) test_pythonlib.py --bytecode-3.2 + SPARK_PARSER_COVERAGE=$(COVER_DIR)/spark-grammar-32.cover $(PYTHON) test_pyenvlib.py --3.2.6 #: Get grammar coverage for Python 3.3 grammar-coverage-3.3: diff --git a/test/bytecode_3.1/10_classdec.pyc b/test/bytecode_3.1/10_classdec.pyc new file mode 100644 index 00000000..25210a17 Binary files /dev/null and b/test/bytecode_3.1/10_classdec.pyc differ diff --git a/test/bytecode_3.3/01_delete_deref.pyc b/test/bytecode_3.3/01_delete_deref.pyc new file mode 100644 index 00000000..06a987bb Binary files /dev/null and b/test/bytecode_3.3/01_delete_deref.pyc differ diff --git a/test/bytecode_3.3/01_triple_compare.pyc b/test/bytecode_3.3/01_triple_compare.pyc new file mode 100644 index 00000000..ad834bc3 Binary files /dev/null and b/test/bytecode_3.3/01_triple_compare.pyc differ diff --git a/test/bytecode_3.3/10_classdec.pyc b/test/bytecode_3.3/10_classdec.pyc new file mode 100644 index 00000000..3c9ff2f4 Binary files /dev/null and b/test/bytecode_3.3/10_classdec.pyc differ diff --git a/test/simple_source/bug33/01_triple_compare.py b/test/simple_source/bug33/01_triple_compare.py new file mode 100644 index 00000000..5523c6b9 --- /dev/null +++ b/test/simple_source/bug33/01_triple_compare.py @@ -0,0 +1,4 @@ +# In Python 3.3+ this uses grammar rule +# cmp_list2 ::= expr COMPARE_OP RETURN_VALUE +def _is_valid_netmask(self, netmask): + return 0 <= netmask <= self._max_prefixlen diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index 62ad2eb4..fe0abc03 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -525,21 +525,14 @@ class PythonParser(GenericASTBuilder): return_lambda ::= ret_expr RETURN_VALUE_LAMBDA LAMBDA_MARKER return_lambda ::= ret_expr RETURN_VALUE_LAMBDA - conditional_lambda ::= expr jmp_false return_if_stmt return_stmt LAMBDA_MARKER + # Doesn't seemt to be used anymore, but other conditional_lambda's are + # conditional_lambda ::= expr jmp_false return_if_stmt return_stmt LAMBDA_MARKER cmp ::= cmp_list cmp ::= compare compare ::= expr expr COMPARE_OP - cmp_list ::= expr cmp_list1 ROT_TWO POP_TOP - _come_from - cmp_list1 ::= expr DUP_TOP ROT_THREE - COMPARE_OP jmp_false - cmp_list1 _come_from - cmp_list1 ::= expr DUP_TOP ROT_THREE - COMPARE_OP jmp_false - cmp_list2 _come_from + cmp_list ::= expr cmp_list1 ROT_TWO POP_TOP _come_from cmp_list2 ::= expr COMPARE_OP JUMP_FORWARD - cmp_list2 ::= expr COMPARE_OP RETURN_VALUE mapexpr ::= BUILD_MAP kvlist kvlist ::= kvlist kv diff --git a/uncompyle6/parsers/parse26.py b/uncompyle6/parsers/parse26.py index 70e77f48..462ad21b 100644 --- a/uncompyle6/parsers/parse26.py +++ b/uncompyle6/parsers/parse26.py @@ -243,7 +243,9 @@ class Python26Parser(Python2Parser): """ conditional ::= expr jmp_false expr jf_cf_pop expr come_from_opt and ::= expr JUMP_IF_FALSE POP_TOP expr JUMP_IF_FALSE POP_TOP - cmp_list ::= expr cmp_list1 ROT_TWO COME_FROM POP_TOP _come_from + cmp_list ::= expr cmp_list1 ROT_TWO COME_FROM POP_TOP _come_from + cmp_list1 ::= expr DUP_TOP ROT_THREE COMPARE_OP jmp_false cmp_list1 _come_from + cmp_list1 ::= expr DUP_TOP ROT_THREE COMPARE_OP jmp_false cmp_list2 _come_from return_if_lambda ::= RETURN_END_IF_LAMBDA POP_TOP conditional_lambda ::= expr jmp_false_then expr return_if_lambda diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index bae130c1..d6642bad 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -336,12 +336,10 @@ class Python3Parser(PythonParser): or ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM - cmp_list1 ::= expr DUP_TOP ROT_THREE - COMPARE_OP JUMP_IF_FALSE_OR_POP - cmp_list1 COME_FROM - cmp_list1 ::= expr DUP_TOP ROT_THREE - COMPARE_OP JUMP_IF_FALSE_OR_POP - cmp_list2 COME_FROM + cmp_list1 ::= expr DUP_TOP ROT_THREE COMPARE_OP JUMP_IF_FALSE_OR_POP + cmp_list1 COME_FROM + cmp_list1 ::= expr DUP_TOP ROT_THREE COMPARE_OP JUMP_IF_FALSE_OR_POP + cmp_list2 COME_FROM """ def p_stmt3(self, args): @@ -440,7 +438,10 @@ class Python3Parser(PythonParser): @staticmethod def call_fn_name(token): """Customize CALL_FUNCTION to add the number of positional arguments""" - return '%s_%i' % (token.kind, token.attr) + if token.attr is not None: + return '%s_%i' % (token.kind, token.attr) + else: + return '%s_0' % (token.kind) def custom_build_class_rule(self, opname, i, token, tokens, customize): ''' @@ -620,8 +621,9 @@ class Python3Parser(PythonParser): call_function ::= expr CALL_METHOD """ seen_LOAD_BUILD_CLASS = False - seen_LOAD_DICTCOMP = False - seen_LOAD_LISTCOMP = False + seen_LOAD_DICTCOMP = False + seen_LOAD_LISTCOMP = False + seen_LOAD_SETCOMP = False # Loop over instructions adding custom grammar rules based on # a specific instruction seen. @@ -634,6 +636,13 @@ class Python3Parser(PythonParser): assign2_pypy ::= expr expr designator designator """, nop_func) + has_get_iter_call_function1 = False + n = len(tokens) + for i, token in enumerate(tokens): + if token == 'GET_ITER' and i < n-2 and self.call_fn_name(tokens[i+1]) == 'CALL_FUNCTION_1': + has_get_iter_call_function1 = True + break + for i, token in enumerate(tokens): opname = token.kind opname_base = opname[:opname.rfind('_')] @@ -766,17 +775,20 @@ class Python3Parser(PythonParser): continue elif opname == 'LOAD_DICTCOMP': seen_LOAD_DICTCOMP = True - rule_pat = ("dictcomp ::= LOAD_DICTCOMP %sMAKE_FUNCTION_0 expr " - "GET_ITER CALL_FUNCTION_1") - self.add_make_function_rule(rule_pat, opname, token.attr, customize) + if has_get_iter_call_function1: + rule_pat = ("dictcomp ::= LOAD_DICTCOMP %sMAKE_FUNCTION_0 expr " + "GET_ITER CALL_FUNCTION_1") + self.add_make_function_rule(rule_pat, opname, token.attr, customize) elif opname == 'LOAD_LISTCOMP': seen_LOAD_LISTCOMP = True continue elif opname == 'LOAD_SETCOMP': + seen_LOAD_SETCOMP = True # Should this be generalized and put under MAKE_FUNCTION? - rule_pat = ("setcomp ::= LOAD_SETCOMP %sMAKE_FUNCTION_0 expr " - "GET_ITER CALL_FUNCTION_1") - self.add_make_function_rule(rule_pat, opname, token.attr, customize) + if has_get_iter_call_function1: + rule_pat = ("setcomp ::= LOAD_SETCOMP %sMAKE_FUNCTION_0 expr " + "GET_ITER CALL_FUNCTION_1") + self.add_make_function_rule(rule_pat, opname, token.attr, customize) elif opname == 'LOOKUP_METHOD': # A PyPy speciality - DRY with parse2 self.add_unique_rule("load_attr ::= expr LOOKUP_METHOD", @@ -787,24 +799,27 @@ class Python3Parser(PythonParser): # Note: this probably doesn't handle kwargs proprerly args_pos, args_kw, annotate_args = token.attr - rule_pat = ("genexpr ::= %sload_closure load_genexpr %%s%s expr " - "GET_ITER CALL_FUNCTION_1" % ('pos_arg '* args_pos, opname)) - self.add_make_function_rule(rule_pat, opname, token.attr, customize) rule_pat = ('mklambda ::= %sload_closure LOAD_LAMBDA %%s%s' % ('pos_arg '* args_pos, opname)) self.add_make_function_rule(rule_pat, opname, token.attr, customize) - rule_pat = ('listcomp ::= %sload_closure LOAD_LISTCOMP %%s%s expr ' - 'GET_ITER CALL_FUNCTION_1' % ('pos_arg ' * args_pos, opname)) - self.add_make_function_rule(rule_pat, opname, token.attr, customize) - rule_pat = ('setcomp ::= %sload_closure LOAD_SETCOMP %%s%s expr ' - 'GET_ITER CALL_FUNCTION_1' % ('pos_arg ' * args_pos, opname)) - self.add_make_function_rule(rule_pat, opname, token.attr, customize) - if seen_LOAD_DICTCOMP: - self.add_unique_rule('dictcomp ::= %sload_closure LOAD_DICTCOMP %s ' - 'expr GET_ITER CALL_FUNCTION_1' % - ('pos_arg '* args_pos, opname), - opname, token.attr, customize) + if has_get_iter_call_function1: + rule_pat = ("genexpr ::= %sload_closure load_genexpr %%s%s expr " + "GET_ITER CALL_FUNCTION_1" % ('pos_arg '* args_pos, opname)) + self.add_make_function_rule(rule_pat, opname, token.attr, customize) + if seen_LOAD_LISTCOMP: + rule_pat = ('listcomp ::= %sload_closure LOAD_LISTCOMP %%s%s expr ' + 'GET_ITER CALL_FUNCTION_1' % ('pos_arg ' * args_pos, opname)) + self.add_make_function_rule(rule_pat, opname, token.attr, customize) + if seen_LOAD_SETCOMP: + rule_pat = ('setcomp ::= %sload_closure LOAD_SETCOMP %%s%s expr ' + 'GET_ITER CALL_FUNCTION_1' % ('pos_arg ' * args_pos, opname)) + self.add_make_function_rule(rule_pat, opname, token.attr, customize) + if seen_LOAD_DICTCOMP: + self.add_unique_rule('dictcomp ::= %sload_closure LOAD_DICTCOMP %s ' + 'expr GET_ITER CALL_FUNCTION_1' % + ('pos_arg '* args_pos, opname), + opname, token.attr, customize) # FIXME: kwarg processing is missing here. # Note order of kwargs and pos args changed between 3.3-3.4 diff --git a/uncompyle6/parsers/parse32.py b/uncompyle6/parsers/parse32.py index 673adaa3..edd57f53 100644 --- a/uncompyle6/parsers/parse32.py +++ b/uncompyle6/parsers/parse32.py @@ -42,6 +42,9 @@ class Python32Parser(Python3Parser): pass def add_custom_rules(self, tokens, customize): + # self.remove_rules(""" + # cmp_list2 ::= expr COMPARE_OP RETURN_VALUE + # """) super(Python32Parser, self).add_custom_rules(tokens, customize) for i, token in enumerate(tokens): opname = token.kind diff --git a/uncompyle6/parsers/parse33.py b/uncompyle6/parsers/parse33.py index bfd5a9bc..516988d8 100644 --- a/uncompyle6/parsers/parse33.py +++ b/uncompyle6/parsers/parse33.py @@ -15,6 +15,8 @@ class Python33Parser(Python32Parser): expr ::= yield_from yield_from ::= expr expr YIELD_FROM + cmp_list2 ::= expr COMPARE_OP RETURN_VALUE + # We do the grammar hackery below for semantics # actions that want c_stmts_opt at index 1