Partial merge

This commit is contained in:
rocky
2024-02-04 12:16:17 -05:00
parent 1e95ebd5f6
commit b0e139e6cc
2 changed files with 67 additions and 53 deletions

View File

@@ -597,12 +597,12 @@ class PythonParser(GenericASTBuilder):
compare ::= compare_single compare ::= compare_single
compare_single ::= expr expr COMPARE_OP compare_single ::= expr expr COMPARE_OP
# A compare_chained is two comparisions, as in: x <= y <= z # A compare_chained is two comparisons, as in: x <= y <= z
compare_chained ::= expr compared_chained_middle ROT_TWO POP_TOP compare_chained ::= expr compared_chained_middle ROT_TWO POP_TOP
_come_froms _come_froms
compare_chained_right ::= expr COMPARE_OP JUMP_FORWARD compare_chained_right ::= expr COMPARE_OP JUMP_FORWARD
# Non-null kvlist items are broken out in the indiviual grammars # Non-null kvlist items are broken out in the individual grammars
kvlist ::= kvlist ::=
# Positional arguments in make_function # Positional arguments in make_function

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2019, 2021-2023 by Rocky Bernstein # Copyright (c) 2015-2019, 2021-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
@@ -74,7 +74,6 @@ from xdis import iscode
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
import uncompyle6.parser as python_parser import uncompyle6.parser as python_parser
from uncompyle6 import parser
from uncompyle6.parsers.treenode import SyntaxTree from uncompyle6.parsers.treenode import SyntaxTree
from uncompyle6.scanner import Code, Token, get_scanner from uncompyle6.scanner import Code, Token, get_scanner
from uncompyle6.semantics import pysource from uncompyle6.semantics import pysource
@@ -88,7 +87,12 @@ from uncompyle6.semantics.consts import (
TABLE_DIRECT, TABLE_DIRECT,
escape, escape,
) )
from uncompyle6.semantics.pysource import ParserError, StringIO from uncompyle6.semantics.pysource import (
DEFAULT_DEBUG_OPTS,
TREE_DEFAULT_DEBUG,
ParserError,
StringIO,
)
from uncompyle6.show import maybe_show_asm, maybe_show_tree from uncompyle6.show import maybe_show_asm, maybe_show_tree
NodeInfo = namedtuple("NodeInfo", "node start finish") NodeInfo = namedtuple("NodeInfo", "node start finish")
@@ -152,10 +156,11 @@ class FragmentsWalker(pysource.SourceWalker, object):
self, self,
version, version,
scanner, scanner,
showast=False, showast=TREE_DEFAULT_DEBUG,
debug_parser=PARSER_DEFAULT_DEBUG, debug_parser=PARSER_DEFAULT_DEBUG,
compile_mode="exec", compile_mode="exec",
is_pypy=False, is_pypy=IS_PYPY,
linestarts={},
tolerate_errors=True, tolerate_errors=True,
): ):
pysource.SourceWalker.__init__( pysource.SourceWalker.__init__(
@@ -167,18 +172,17 @@ class FragmentsWalker(pysource.SourceWalker, object):
debug_parser=debug_parser, debug_parser=debug_parser,
compile_mode=compile_mode, compile_mode=compile_mode,
is_pypy=is_pypy, is_pypy=is_pypy,
linestarts=linestarts,
tolerate_errors=tolerate_errors, tolerate_errors=tolerate_errors,
) )
# Hide_internal suppresses displaying the additional instructions that sometimes # hide_internal suppresses displaying the additional instructions that sometimes
# exist in code but but were not written in the source code. # exist in code but but were not written in the source code.
# An example is: # An example is:
# __module__ = __name__ # __module__ = __name__
# # If showing source code we generally don't want to show this. However in fragment
# If showing source code we generally don't want to show this. However # deparsing we generally do need to see these instructions since we may be stopped
# in fragment deparsing we generally do need to see these instructions # at one. So here we do not want to suppress showing such instructions.
# since we may be stopped at one. So here we do not want to suppress
# showing such instructions.
self.hide_internal = False self.hide_internal = False
self.offsets = {} self.offsets = {}
self.last_finish = -1 self.last_finish = -1
@@ -659,7 +663,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
n = ast[iter_index] n = ast[iter_index]
assert n == "comp_iter" assert n == "comp_iter", n.kind
# Find the comprehension body. It is the inner-most # Find the comprehension body. It is the inner-most
# node that is not list_.. . # node that is not list_.. .
while n == "comp_iter": # list_iter while n == "comp_iter": # list_iter
@@ -718,7 +722,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
assert iscode(code), node[code_index] assert iscode(code), node[code_index]
code_name = code.co_name code_name = code.co_name
code = Code(code, self.scanner, self.currentclass) code = Code(code, self.scanner, self.currentclass, self.debug_opts["asm"])
ast = self.build_ast(code._tokens, code._customize, code) ast = self.build_ast(code._tokens, code._customize, code)
@@ -1065,13 +1069,17 @@ class FragmentsWalker(pysource.SourceWalker, object):
# Python 3.2 works like this # Python 3.2 works like this
subclass = load_closure[-2].attr subclass = load_closure[-2].attr
else: else:
raise "Internal Error n_classdef: cannot find class body" raise RuntimeError(
"Internal Error n_classdef: cannot find class body"
)
if hasattr(buildclass[3], "__len__"): if hasattr(buildclass[3], "__len__"):
subclass_info = buildclass[3] subclass_info = buildclass[3]
elif hasattr(buildclass[2], "__len__"): elif hasattr(buildclass[2], "__len__"):
subclass_info = buildclass[2] subclass_info = buildclass[2]
else: else:
raise "Internal Error n_classdef: cannot superclass name" raise RuntimeError(
"Internal Error n_classdef: cannot superclass name"
)
else: else:
subclass = buildclass[1][0].attr subclass = buildclass[1][0].attr
subclass_info = node[0] subclass_info = node[0]
@@ -1119,7 +1127,13 @@ class FragmentsWalker(pysource.SourceWalker, object):
n_classdefdeco2 = n_classdef n_classdefdeco2 = n_classdef
def gen_source( def gen_source(
self, ast, name, customize, is_lambda=False, returnNone=False, debug_opts=None self,
ast,
name,
customize,
is_lambda=False,
returnNone=False,
debug_opts=DEFAULT_DEBUG_OPTS,
): ):
"""convert parse tree to Python source code""" """convert parse tree to Python source code"""
@@ -1205,7 +1219,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.p.insts = self.scanner.insts self.p.insts = self.scanner.insts
self.p.offset2inst_index = self.scanner.offset2inst_index self.p.offset2inst_index = self.scanner.offset2inst_index
self.p.opc = self.scanner.opc self.p.opc = self.scanner.opc
ast = parser.parse(self.p, tokens, customize, code) ast = python_parser.parse(self.p, tokens, customize, code)
self.p.insts = p_insts self.p.insts = p_insts
except (python_parser.ParserError, AssertionError) as e: except (python_parser.ParserError, AssertionError) as e:
raise ParserError(e, tokens, {}) raise ParserError(e, tokens, {})
@@ -1348,7 +1362,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
selectedText = text[start:finish] selectedText = text[start:finish]
# Compute offsets relative to the beginning of the # Compute offsets relative to the beginning of the
# line rather than the beinning of the text # line rather than the beginning of the text.
try: try:
lineStart = text[:start].rindex("\n") + 1 lineStart = text[:start].rindex("\n") + 1
except ValueError: except ValueError:
@@ -1356,7 +1370,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
adjustedStart = start - lineStart adjustedStart = start - lineStart
# If selected text is greater than a single line # If selected text is greater than a single line
# just show the first line plus elipses. # just show the first line plus ellipsis.
lines = selectedText.split("\n") lines = selectedText.split("\n")
if len(lines) > 1: if len(lines) > 1:
adjustedEnd = len(lines[0]) - adjustedStart adjustedEnd = len(lines[0]) - adjustedStart
@@ -1429,7 +1443,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
p = node.parent p = node.parent
orig_parent = p orig_parent = p
# If we can get different text, use that as the parent, # If we can get different text, use that as the parent,
# otherwise we'll use the immeditate parent # otherwise we'll use the immediatate parent.
while p and ( while p and (
hasattr(p, "parent") and p.start == node.start and p.finish == node.finish hasattr(p, "parent") and p.start == node.start and p.finish == node.finish
): ):
@@ -1566,19 +1580,19 @@ class FragmentsWalker(pysource.SourceWalker, object):
if node[0].kind.startswith("kvlist"): if node[0].kind.startswith("kvlist"):
# Python 3.5+ style key/value list in dict # Python 3.5+ style key/value list in dict
kv_node = node[0] kv_node = node[0]
l = list(kv_node) ll = list(kv_node)
length = len(l) length = len(ll)
if kv_node[-1].kind.startswith("BUILD_MAP"): if kv_node[-1].kind.startswith("BUILD_MAP"):
length -= 1 length -= 1
i = 0 i = 0
while i < length: while i < length:
self.write(sep) self.write(sep)
name = self.traverse(l[i], indent="") name = self.traverse(ll[i], indent="")
l[i].parent = kv_node ll[i].parent = kv_node
l[i + 1].parent = kv_node ll[i + 1].parent = kv_node
self.write(name, ": ") self.write(name, ": ")
value = self.traverse( value = self.traverse(
l[i + 1], indent=self.indent + (len(name) + 2) * " " ll[i + 1], indent=self.indent + (len(name) + 2) * " "
) )
self.write(sep, name, ": ", value) self.write(sep, name, ": ", value)
sep = line_seperator sep = line_seperator
@@ -1588,25 +1602,25 @@ class FragmentsWalker(pysource.SourceWalker, object):
elif len(node) > 1 and node[1].kind.startswith("kvlist"): elif len(node) > 1 and node[1].kind.startswith("kvlist"):
# Python 3.0..3.4 style key/value list in dict # Python 3.0..3.4 style key/value list in dict
kv_node = node[1] kv_node = node[1]
l = list(kv_node) ll = list(kv_node)
if len(l) > 0 and l[0].kind == "kv3": if len(ll) > 0 and ll[0].kind == "kv3":
# Python 3.2 does this # Python 3.2 does this
kv_node = node[1][0] kv_node = node[1][0]
l = list(kv_node) ll = list(kv_node)
i = 0 i = 0
while i < len(l): while i < len(ll):
l[i].parent = kv_node ll[i].parent = kv_node
l[i + 1].parent = kv_node ll[i + 1].parent = kv_node
key_start = len(self.f.getvalue()) + len(sep) key_start = len(self.f.getvalue()) + len(sep)
name = self.traverse(l[i + 1], indent="") name = self.traverse(ll[i + 1], indent="")
key_finish = key_start + len(name) key_finish = key_start + len(name)
val_start = key_finish + 2 val_start = key_finish + 2
value = self.traverse( value = self.traverse(
l[i], indent=self.indent + (len(name) + 2) * " " ll[i], indent=self.indent + (len(name) + 2) * " "
) )
self.write(sep, name, ": ", value) self.write(sep, name, ": ", value)
self.set_pos_info_recurse(l[i + 1], key_start, key_finish) self.set_pos_info_recurse(ll[i + 1], key_start, key_finish)
self.set_pos_info_recurse(l[i], val_start, val_start + len(value)) self.set_pos_info_recurse(ll[i], val_start, val_start + len(value))
sep = line_seperator sep = line_seperator
i += 3 i += 3
pass pass
@@ -1779,7 +1793,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
n_set = n_tuple = n_build_set = n_list n_set = n_tuple = n_build_set = n_list
def template_engine(self, entry, startnode): def template_engine(self, entry, startnode):
"""The format template interpetation engine. See the comment at the """The format template interpretation engine. See the comment at the
beginning of this module for the how we interpret format beginning of this module for the how we interpret format
specifications such as %c, %C, and so on. specifications such as %c, %C, and so on.
""" """
@@ -1810,7 +1824,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
if m.group("child"): if m.group("child"):
node = node[int(m.group("child"))] node = node[int(m.group("child"))]
node.parent = startnode node.parent = startnode
except: except Exception:
print(node.__dict__) print(node.__dict__)
raise raise
@@ -1947,7 +1961,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
start = len(self.f.getvalue()) start = len(self.f.getvalue())
self.write(eval(expr, d, d)) self.write(eval(expr, d, d))
self.set_pos_info(node, start, len(self.f.getvalue())) self.set_pos_info(node, start, len(self.f.getvalue()))
except: except Exception:
print(node) print(node)
raise raise
m = escape.search(fmt, i) m = escape.search(fmt, i)
@@ -1962,7 +1976,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
# FIXME figure out how to get these cases to be table driven. # FIXME figure out how to get these cases to be table driven.
# 2. subroutine calls. It the last op is the call and for purposes of printing # 2. subroutine calls. It the last op is the call and for purposes of printing
# we don't need to print anything special there. However it encompases the # we don't need to print anything special there. However it encompasses the
# entire string of the node fn(...) # entire string of the node fn(...)
if startnode.kind == "call": if startnode.kind == "call":
last_node = startnode[-1] last_node = startnode[-1]
@@ -1997,7 +2011,7 @@ def deparse_code(
showgrammar=False, showgrammar=False,
code_objects={}, code_objects={},
compile_mode="exec", compile_mode="exec",
is_pypy=None, is_pypy=IS_PYPY,
walker=FragmentsWalker, walker=FragmentsWalker,
): ):
debug_opts = {"asm": showasm, "ast": showast, "grammar": showgrammar} debug_opts = {"asm": showasm, "ast": showast, "grammar": showgrammar}
@@ -2050,7 +2064,7 @@ def code_deparse(
is_pypy = IS_PYPY is_pypy = IS_PYPY
# store final output stream for case of error # store final output stream for case of error
scanner = get_scanner(version, is_pypy=is_pypy) scanner = get_scanner(version, is_pypy=is_pypy, show_asm=debug_opts["asm"])
show_asm = debug_opts.get("asm", None) show_asm = debug_opts.get("asm", None)
tokens, customize = scanner.ingest(co, code_objects=code_objects, show_asm=show_asm) tokens, customize = scanner.ingest(co, code_objects=code_objects, show_asm=show_asm)
@@ -2066,14 +2080,15 @@ def code_deparse(
# Build Syntax Tree from tokenized and massaged disassembly. # Build Syntax Tree from tokenized and massaged disassembly.
# deparsed = pysource.FragmentsWalker(out, scanner, showast=showast) # deparsed = pysource.FragmentsWalker(out, scanner, showast=showast)
show_ast = debug_opts.get("ast", None) show_tree = debug_opts.get("tree", False)
deparsed = walker( deparsed = walker(
version, version,
scanner, scanner,
showast=show_ast, showast=show_tree,
debug_parser=debug_parser, debug_parser=debug_parser,
compile_mode=compile_mode, compile_mode=compile_mode,
is_pypy=is_pypy, is_pypy=is_pypy,
linestarts=linestarts,
) )
is_top_level_module = co.co_name == "<module>" is_top_level_module = co.co_name == "<module>"
@@ -2094,7 +2109,7 @@ def code_deparse(
) )
# Just when you think we've forgotten about what we # Just when you think we've forgotten about what we
# were supposed to to: Generate source from the Syntax ree! # were supposed to do: Generate source from the Syntax tree!
deparsed.gen_source(deparsed.ast, co.co_name, customize) deparsed.gen_source(deparsed.ast, co.co_name, customize)
deparsed.set_pos_info(deparsed.ast, 0, len(deparsed.text)) deparsed.set_pos_info(deparsed.ast, 0, len(deparsed.text))
@@ -2149,7 +2164,7 @@ def code_deparse_around_offset(
assert iscode(co) assert iscode(co)
if version is None: if version is None:
version = sys.version_info[:3] version = PYTHON_VERSION_TRIPLE
if is_pypy is None: if is_pypy is None:
is_pypy = IS_PYPY is_pypy = IS_PYPY
@@ -2167,7 +2182,7 @@ def code_deparse_around_offset(
return deparsed return deparsed
# Deprecated. Here still for compatability # Deprecated. Here still for compatibility
def deparse_code_around_offset( def deparse_code_around_offset(
name, name,
offset, offset,
@@ -2176,7 +2191,7 @@ def deparse_code_around_offset(
out=StringIO(), out=StringIO(),
showasm=False, showasm=False,
showast=False, showast=False,
showgrammar=False, showgrammar=PARSER_DEFAULT_DEBUG,
is_pypy=False, is_pypy=False,
): ):
debug_opts = {"asm": showasm, "ast": showast, "grammar": showgrammar} debug_opts = {"asm": showasm, "ast": showast, "grammar": showgrammar}
@@ -2313,6 +2328,5 @@ def deparsed_find(tup, deparsed, code):
# # deparse_test(get_code_for_fn(FragmentsWalker.fixup_offsets)) # # deparse_test(get_code_for_fn(FragmentsWalker.fixup_offsets))
# # deparse_test(get_code_for_fn(FragmentsWalker.n_list)) # # deparse_test(get_code_for_fn(FragmentsWalker.n_list))
# print("=" * 30) # print("=" * 30)
# # deparse_test_around(408, 'n_list', # # deparse_test_around(408, 'n_list', get_code_for_fn(FragmentsWalker.n_build_list))
# get_code_for_fn(FragmentsWalker.n_build_list))
# # deparse_test(inspect.currentframe().f_code) # # deparse_test(inspect.currentframe().f_code)