Some limited support for 3.8 "=" specifier

This commit is contained in:
rocky
2022-07-06 13:00:52 -04:00
parent cc4ea47d24
commit 5a4136a7f6
9 changed files with 248 additions and 5 deletions

View File

@@ -20,6 +20,7 @@ from __future__ import print_function
from uncompyle6.parser import PythonParserSingle, nop_func
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
from uncompyle6.parsers.parse37 import Python37Parser
from uncompyle6.parsers.reducecheck import joined_str_check
class Python38Parser(Python37Parser):
def p_38_stmt(self, args):
@@ -521,6 +522,29 @@ class Python38Parser(Python37Parser):
self.add_unique_rules(["expr ::= %s" % collection, rule], customize)
continue
continue
elif opname == "BUILD_STRING_2":
self.addRule(
"""
expr ::= formatted_value_debug
formatted_value_debug ::= LOAD_STR formatted_value2 BUILD_STRING_2
formatted_value_debug ::= LOAD_STR formatted_value1 BUILD_STRING_2
""",
nop_func,
)
custom_ops_processed.add(opname)
elif opname == "BUILD_STRING_3":
self.addRule(
"""
expr ::= formatted_value_debug
formatted_value_debug ::= LOAD_STR formatted_value2 LOAD_STR BUILD_STRING_3
formatted_value_debug ::= LOAD_STR formatted_value1 LOAD_STR BUILD_STRING_3
""",
nop_func,
)
custom_ops_processed.add(opname)
elif opname == "LOAD_CLOSURE":
self.addRule("""load_closure ::= LOAD_CLOSURE+""", nop_func)

View File

@@ -1,8 +1,22 @@
# Copyright (c) 2020 Rocky Bernstein
# Copyright (c) 2020, 2022 Rocky Bernstein
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
def iflaststmt(self, lhs, n, rule, ast, tokens, first, last):
testexpr = ast[0]
def iflaststmt(
self, lhs: str, n: int, rule, tree, tokens: list, first: int, last: int
) -> bool:
testexpr = tree[0]
if testexpr[0] in ("testtrue", "testfalse"):

View File

@@ -0,0 +1,47 @@
# Copyright (c) 2022 Rocky Bernstein
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
def joined_str_ok(
self, lhs: str, n: int, rule, tree, tokens: list, first: int, last: int
) -> bool:
# In Python 3.8, there is a new "=" specifier.
# See https://docs.python.org/3/whatsnew/3.8.html#f-strings-support-for-self-documenting-expressions-and-debugging
# We detect this here inside joined_str by looking for an
# expr->LOAD_STR which has an "=" added at the end
# and is equal without the "=" to expr->formated_value2->LOAD_CONST
# converted to a string.
expr1 = tree[0]
if expr1 != "expr":
return False
load_str = expr1[0]
if load_str != "LOAD_STR":
return False
format_value_equal = load_str.attr
if format_value_equal[-1] != "=":
return False
expr2 = tree[1]
if expr2 != "expr":
return False
formatted_value = expr2[0]
if not formatted_value.kind.startswith("formatted_value"):
return False
expr2a = formatted_value[0]
if expr2a != "expr":
return False
load_const = expr2a[0]
if load_const == "LOAD_CONST":
format_value2 = load_const.attr
return str(format_value2) == format_value_equal[:-1]
return True

View File

@@ -0,0 +1,31 @@
# Copyright (c) 2020 Rocky Bernstein
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
def whilestmt(
self, lhs: str, n: int, rule, tree, tokens: list, first: int, last: int
) -> bool:
# When we are missing a COME_FROM_LOOP, the
# "while" statement is nested inside an if/else
# so after the POP_BLOCK we have a JUMP_FORWARD which forms the "else" portion of the "if"
# Check this.
# print("XXX", first, last, rule)
# for t in range(first, last): print(tokens[t])
# print("="*40)
return tokens[last - 1] == "POP_BLOCK" and tokens[last] not in (
"JUMP_FORWARD",
"COME_FROM_LOOP",
"COME_FROM",
)

View File

@@ -0,0 +1,41 @@
# Copyright (c) 2022 Rocky Bernstein
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
def whilestmt38_check(
self, lhs: str, n: int, rule, ast, tokens: list, first: int, last: int
) -> bool:
# When we are missing a COME_FROM_LOOP, the
# "while" statement is nested inside an if/else
# so after the POP_BLOCK we have a JUMP_FORWARD which forms the "else" portion of the "if"
# Check this.
# print("XXX", first, last, rule)
# for t in range(first, last):
# print(tokens[t])
# print("=" * 40)
if tokens[last] != "COME_FROM" and tokens[last - 1] == "COME_FROM":
last -= 1
if tokens[last - 1].kind.startswith("RAISE_VARARGS"):
return True
while tokens[last] == "COME_FROM":
last -= 1
# In a "while" loop, (in contrast to "for" loop), the loop jump is
# always to the first offset
first_offset = tokens[first].off2int()
if tokens[last] == "JUMP_LOOP" and (
tokens[last].attr == first_offset or tokens[last - 1].attr == first_offset
):
return False
return True