You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Bugs found in 3.0 decomplation...
parsers/parse30.py; fix set comprehension grammar bug uncompyle6/semantics/n_actions.py: evidence of the evils of modifying node data (via node.pop)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2016-2017, 2022-2023 Rocky Bernstein
|
# Copyright (c) 2016-2017, 2022-2024 Rocky Bernstein
|
||||||
"""
|
"""
|
||||||
spark grammar differences over Python 3.1 for Python 3.0.
|
spark grammar differences over Python 3.1 for Python 3.0.
|
||||||
"""
|
"""
|
||||||
@@ -7,8 +7,8 @@ from __future__ import print_function
|
|||||||
from uncompyle6.parser import PythonParserSingle
|
from uncompyle6.parser import PythonParserSingle
|
||||||
from uncompyle6.parsers.parse31 import Python31Parser
|
from uncompyle6.parsers.parse31 import Python31Parser
|
||||||
|
|
||||||
class Python30Parser(Python31Parser):
|
|
||||||
|
|
||||||
|
class Python30Parser(Python31Parser):
|
||||||
def p_30(self, args):
|
def p_30(self, args):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -78,7 +78,6 @@ class Python30Parser(Python31Parser):
|
|||||||
set_comp_func ::= set_comp_header
|
set_comp_func ::= set_comp_header
|
||||||
LOAD_ARG FOR_ITER store comp_iter
|
LOAD_ARG FOR_ITER store comp_iter
|
||||||
JUMP_BACK ending_return
|
JUMP_BACK ending_return
|
||||||
RETURN_VALUE RETURN_LAST
|
|
||||||
|
|
||||||
list_comp_header ::= BUILD_LIST_0 DUP_TOP STORE_FAST
|
list_comp_header ::= BUILD_LIST_0 DUP_TOP STORE_FAST
|
||||||
list_comp ::= list_comp_header
|
list_comp ::= list_comp_header
|
||||||
@@ -118,7 +117,7 @@ class Python30Parser(Python31Parser):
|
|||||||
# From Python 2.6
|
# From Python 2.6
|
||||||
|
|
||||||
|
|
||||||
lc_body ::= LOAD_FAST expr LIST_APPEND
|
lc_body ::= LOAD_FAST expr LIST_APPEND
|
||||||
lc_body ::= LOAD_NAME expr LIST_APPEND
|
lc_body ::= LOAD_NAME expr LIST_APPEND
|
||||||
list_if ::= expr jmp_false_then list_iter
|
list_if ::= expr jmp_false_then list_iter
|
||||||
list_if_not ::= expr jmp_true list_iter JUMP_BACK come_froms POP_TOP
|
list_if_not ::= expr jmp_true list_iter JUMP_BACK come_froms POP_TOP
|
||||||
@@ -216,9 +215,9 @@ class Python30Parser(Python31Parser):
|
|||||||
compare_chained_right ::= expr COMPARE_OP RETURN_END_IF
|
compare_chained_right ::= expr COMPARE_OP RETURN_END_IF
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def remove_rules_30(self):
|
def remove_rules_30(self):
|
||||||
self.remove_rules("""
|
self.remove_rules(
|
||||||
|
"""
|
||||||
|
|
||||||
# The were found using grammar coverage
|
# The were found using grammar coverage
|
||||||
while1stmt ::= SETUP_LOOP l_stmts COME_FROM JUMP_BACK COME_FROM_LOOP
|
while1stmt ::= SETUP_LOOP l_stmts COME_FROM JUMP_BACK COME_FROM_LOOP
|
||||||
@@ -286,29 +285,30 @@ class Python30Parser(Python31Parser):
|
|||||||
or ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM
|
or ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM
|
||||||
and ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM
|
and ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM
|
||||||
and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM
|
and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
def customize_grammar_rules(self, tokens, customize):
|
def customize_grammar_rules(self, tokens, customize):
|
||||||
super(Python30Parser, self).customize_grammar_rules(tokens, customize)
|
super(Python30Parser, self).customize_grammar_rules(tokens, customize)
|
||||||
self.remove_rules_30()
|
self.remove_rules_30()
|
||||||
|
|
||||||
self.check_reduce["iflaststmtl"] = "AST"
|
self.check_reduce["iflaststmtl"] = "AST"
|
||||||
self.check_reduce['ifstmt'] = "AST"
|
self.check_reduce["ifstmt"] = "AST"
|
||||||
self.check_reduce["ifelsestmtc"] = "AST"
|
self.check_reduce["ifelsestmtc"] = "AST"
|
||||||
self.check_reduce["ifelsestmt"] = "AST"
|
self.check_reduce["ifelsestmt"] = "AST"
|
||||||
# self.check_reduce["and"] = "stmt"
|
# self.check_reduce["and"] = "stmt"
|
||||||
return
|
return
|
||||||
|
|
||||||
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||||
invalid = super(Python30Parser,
|
invalid = super(Python30Parser, self).reduce_is_invalid(
|
||||||
self).reduce_is_invalid(rule, ast,
|
rule, ast, tokens, first, last
|
||||||
tokens, first, last)
|
)
|
||||||
if invalid:
|
if invalid:
|
||||||
return invalid
|
return invalid
|
||||||
lhs = rule[0]
|
lhs = rule[0]
|
||||||
if (
|
if (
|
||||||
lhs in ("iflaststmtl", "ifstmt",
|
lhs in ("iflaststmtl", "ifstmt", "ifelsestmt", "ifelsestmtc")
|
||||||
"ifelsestmt", "ifelsestmtc") and ast[0] == "testexpr"
|
and ast[0] == "testexpr"
|
||||||
):
|
):
|
||||||
testexpr = ast[0]
|
testexpr = ast[0]
|
||||||
if testexpr[0] == "testfalse":
|
if testexpr[0] == "testfalse":
|
||||||
@@ -316,7 +316,10 @@ class Python30Parser(Python31Parser):
|
|||||||
if lhs == "ifelsestmtc" and ast[2] == "jump_absolute_else":
|
if lhs == "ifelsestmtc" and ast[2] == "jump_absolute_else":
|
||||||
jump_absolute_else = ast[2]
|
jump_absolute_else = ast[2]
|
||||||
come_from = jump_absolute_else[2]
|
come_from = jump_absolute_else[2]
|
||||||
return come_from == "COME_FROM" and come_from.attr < tokens[first].offset
|
return (
|
||||||
|
come_from == "COME_FROM"
|
||||||
|
and come_from.attr < tokens[first].offset
|
||||||
|
)
|
||||||
pass
|
pass
|
||||||
elif lhs in ("ifelsestmt", "ifelsestmtc") and ast[2] == "jump_cf_pop":
|
elif lhs in ("ifelsestmt", "ifelsestmtc") and ast[2] == "jump_cf_pop":
|
||||||
jump_cf_pop = ast[2]
|
jump_cf_pop = ast[2]
|
||||||
@@ -339,11 +342,11 @@ class Python30Parser(Python31Parser):
|
|||||||
jmp_false = testfalse[1]
|
jmp_false = testfalse[1]
|
||||||
if last == len(tokens):
|
if last == len(tokens):
|
||||||
last -= 1
|
last -= 1
|
||||||
while (isinstance(tokens[first].offset, str) and first < last):
|
while isinstance(tokens[first].offset, str) and first < last:
|
||||||
first += 1
|
first += 1
|
||||||
if first == last:
|
if first == last:
|
||||||
return True
|
return True
|
||||||
while (first < last and isinstance(tokens[last].offset, str)):
|
while first < last and isinstance(tokens[last].offset, str):
|
||||||
last -= 1
|
last -= 1
|
||||||
if rule[0] == "iflaststmtl":
|
if rule[0] == "iflaststmtl":
|
||||||
return not (jmp_false[0].attr <= tokens[last].offset)
|
return not (jmp_false[0].attr <= tokens[last].offset)
|
||||||
@@ -351,8 +354,9 @@ class Python30Parser(Python31Parser):
|
|||||||
jmp_false_target = jmp_false[0].attr
|
jmp_false_target = jmp_false[0].attr
|
||||||
if tokens[first].offset > jmp_false_target:
|
if tokens[first].offset > jmp_false_target:
|
||||||
return True
|
return True
|
||||||
return (
|
return (jmp_false_target > tokens[last].offset) and tokens[
|
||||||
(jmp_false_target > tokens[last].offset) and tokens[last] != "JUMP_FORWARD")
|
last
|
||||||
|
] != "JUMP_FORWARD"
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
@@ -361,33 +365,43 @@ class Python30Parser(Python31Parser):
|
|||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Python30ParserSingle(Python30Parser, PythonParserSingle):
|
class Python30ParserSingle(Python30Parser, PythonParserSingle):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
# Check grammar
|
# Check grammar
|
||||||
p = Python30Parser()
|
p = Python30Parser()
|
||||||
p.remove_rules_30()
|
p.remove_rules_30()
|
||||||
p.check_grammar()
|
p.check_grammar()
|
||||||
from xdis.version_info import PYTHON_VERSION_TRIPLE, IS_PYPY
|
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
|
||||||
|
|
||||||
if PYTHON_VERSION_TRIPLE[:2] == (3, 0):
|
if PYTHON_VERSION_TRIPLE[:2] == (3, 0):
|
||||||
lhs, rhs, tokens, right_recursive, dup_rhs = p.check_sets()
|
lhs, rhs, tokens, right_recursive, dup_rhs = p.check_sets()
|
||||||
from uncompyle6.scanner import get_scanner
|
from uncompyle6.scanner import get_scanner
|
||||||
|
|
||||||
s = get_scanner(PYTHON_VERSION_TRIPLE, IS_PYPY)
|
s = get_scanner(PYTHON_VERSION_TRIPLE, IS_PYPY)
|
||||||
opcode_set = set(s.opc.opname).union(set(
|
opcode_set = set(s.opc.opname).union(
|
||||||
"""JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
|
set(
|
||||||
|
"""JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
|
||||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_CLASSNAME
|
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_CLASSNAME
|
||||||
LAMBDA_MARKER RETURN_LAST
|
LAMBDA_MARKER RETURN_LAST
|
||||||
""".split()))
|
""".split()
|
||||||
|
)
|
||||||
|
)
|
||||||
## FIXME: try this
|
## FIXME: try this
|
||||||
remain_tokens = set(tokens) - opcode_set
|
remain_tokens = set(tokens) - opcode_set
|
||||||
import re
|
import re
|
||||||
remain_tokens = set([re.sub(r'_\d+$', '', t) for t in remain_tokens])
|
|
||||||
remain_tokens = set([re.sub('_CONT$', '', t) for t in remain_tokens])
|
remain_tokens = set([re.sub(r"_\d+$", "", t) for t in remain_tokens])
|
||||||
|
remain_tokens = set([re.sub("_CONT$", "", t) for t in remain_tokens])
|
||||||
remain_tokens = set(remain_tokens) - opcode_set
|
remain_tokens = set(remain_tokens) - opcode_set
|
||||||
print(remain_tokens)
|
print(remain_tokens)
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
from spark_parser.spark import rule2str
|
from spark_parser.spark import rule2str
|
||||||
|
|
||||||
for rule in sorted(p.rule2name.items()):
|
for rule in sorted(p.rule2name.items()):
|
||||||
print(rule2str(rule[0]))
|
print(rule2str(rule[0]))
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2022-2023 by Rocky Bernstein
|
# Copyright (c) 2022-2024 by Rocky Bernstein
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,22 +16,12 @@
|
|||||||
Custom Nonterminal action functions. See NonterminalActions docstring.
|
Custom Nonterminal action functions. See NonterminalActions docstring.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from uncompyle6.semantics.consts import (
|
|
||||||
INDENT_PER_LEVEL,
|
|
||||||
NONE,
|
|
||||||
PRECEDENCE,
|
|
||||||
minint,
|
|
||||||
)
|
|
||||||
|
|
||||||
from uncompyle6.parsers.treenode import SyntaxTree
|
from uncompyle6.parsers.treenode import SyntaxTree
|
||||||
from uncompyle6.scanners.tok import Token
|
from uncompyle6.scanners.tok import Token
|
||||||
|
from uncompyle6.semantics.consts import INDENT_PER_LEVEL, NONE, PRECEDENCE, minint
|
||||||
|
from uncompyle6.semantics.helper import find_code_node, flatten_list
|
||||||
from uncompyle6.util import better_repr, get_code_name
|
from uncompyle6.util import better_repr, get_code_name
|
||||||
|
|
||||||
from uncompyle6.semantics.helper import (
|
|
||||||
find_code_node,
|
|
||||||
flatten_list,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NonterminalActions:
|
class NonterminalActions:
|
||||||
"""
|
"""
|
||||||
@@ -826,7 +816,8 @@ class NonterminalActions:
|
|||||||
|
|
||||||
p = self.prec
|
p = self.prec
|
||||||
self.prec = PRECEDENCE["yield"] - 1
|
self.prec = PRECEDENCE["yield"] - 1
|
||||||
lastnode = node.pop()
|
lastnode = node[-1]
|
||||||
|
node = node[:-1]
|
||||||
lastnodetype = lastnode.kind
|
lastnodetype = lastnode.kind
|
||||||
|
|
||||||
# If this build list is inside a CALL_FUNCTION_VAR,
|
# If this build list is inside a CALL_FUNCTION_VAR,
|
||||||
|
Reference in New Issue
Block a user