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:
rocky
2024-02-12 00:58:42 -05:00
parent ca04ae98f7
commit 02ed25e7cb
2 changed files with 44 additions and 39 deletions

View File

@@ -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]))

View File

@@ -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,