Skip to content

Commit

Permalink
Transition decompiler to python 3.
Browse files Browse the repository at this point in the history
  • Loading branch information
CensoredUsername committed Feb 20, 2024
1 parent 8af0dd8 commit a2d1343
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 67 deletions.
40 changes: 21 additions & 19 deletions decompiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,21 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import unicode_literals
from util import DecompilerBase, First, WordConcatenator, reconstruct_paraminfo, \
reconstruct_arginfo, string_escape, split_logical_lines, Dispatcher, \
say_get_code, OptionBase
from .util import DecompilerBase, First, WordConcatenator, reconstruct_paraminfo, \
reconstruct_arginfo, string_escape, split_logical_lines, Dispatcher, \
say_get_code, OptionBase

from operator import itemgetter
from StringIO import StringIO
from io import StringIO

import magic
magic.fake_package(b"renpy")
from . import magic
magic.fake_package("renpy")
import renpy

import sl2decompiler
import testcasedecompiler
import atldecompiler
import codegen
import astdump
from . import sl2decompiler
from . import testcasedecompiler
from . import atldecompiler
from . import astdump

__all__ = ["astdump", "codegen", "magic", "sl2decompiler", "testcasedecompiler", "translate", "util", "Options", "pprint", "Decompiler"]

Expand Down Expand Up @@ -147,7 +145,8 @@ def print_imspec(self, imspec):
if len(imspec[6]) > 0:
words.append("behind %s" % ', '.join(imspec[6]))

if isinstance(imspec[4], unicode):
# todo: this check probably doesn't work in ren'py 8
if isinstance(imspec[4], str):
words.append("onlayer %s" % imspec[4])

if imspec[5] is not None:
Expand Down Expand Up @@ -226,7 +225,8 @@ def print_scene(self, ast):
self.write("scene")

if ast.imspec is None:
if isinstance(ast.layer, unicode):
# todo: this check probably doesn't work in ren'py 8
if isinstance(ast.layer, str):
self.write(" onlayer %s" % ast.layer)
needs_space = True
else:
Expand Down Expand Up @@ -385,7 +385,7 @@ def print_if(self, ast):
for i, (condition, block) in enumerate(ast.entries):
# The non-Unicode string "True" is the condition for else:.
# todo: this probably isn't true anymore for 8.0/7.5 and upwards.
if (i + 1) == len(ast.entries) and not isinstance(condition, unicode):
if (i + 1) == len(ast.entries) and not isinstance(condition, str):
self.indent()
self.write("else:")
else:
Expand Down Expand Up @@ -524,7 +524,8 @@ def print_menu_item(self, label, condition, block, arguments):
self.write(reconstruct_arginfo(arguments))

if block is not None:
if isinstance(condition, unicode):
# todo: this check probably doesn't work in ren'py 8
if isinstance(condition, str):
self.write(" if %s" % condition)
self.write(":")
self.print_nodes(block, 1)
Expand Down Expand Up @@ -559,8 +560,9 @@ def print_menu(self, ast):

# if the condition is a unicode subclass with a "linenumber" attribute it was script.
# If it isn't ren'py used to insert a "True" string. This string used to be of type str
# but nowadays it's of time unicode, just not of type PyExpr
if isinstance(condition, unicode) and hasattr(condition, "linenumber"):
# but nowadays it's of type unicode, just not of type PyExpr
# todo: this check probably doesn't work in ren'py 8
if isinstance(condition, str) and hasattr(condition, "linenumber"):
if self.say_inside_menu is not None and condition.linenumber > self.linenumber + 1:
# The easy case: we know the line number that the menu item is on, because the condition tells us
# So we put the say statement here if there's room for it, or don't if there's not
Expand Down Expand Up @@ -720,7 +722,7 @@ def print_style(self, ast):
if ast.variant.linenumber not in keywords:
keywords[ast.variant.linenumber] = WordConcatenator(False)
keywords[ast.variant.linenumber].append("variant %s" % ast.variant)
for key, value in ast.properties.iteritems():
for key, value in ast.properties.items():
if value.linenumber not in keywords:
keywords[value.linenumber] = WordConcatenator(False)
keywords[value.linenumber].append("%s %s" % (key, value))
Expand Down
49 changes: 36 additions & 13 deletions decompiler/astdump.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import unicode_literals

import sys
import inspect
import ast as py_ast
Expand All @@ -40,7 +38,7 @@ class AstDumper(object):
MAP_CLOSE = {list: ']', tuple: ')', set: '})', frozenset: '})'}

def __init__(self, out_file=None, no_pyexpr=False,
comparable=False, indentation=u' '):
comparable=False, indentation=" "):
self.indentation = indentation
self.out_file = out_file or sys.stdout
self.comparable = comparable
Expand Down Expand Up @@ -70,9 +68,11 @@ def print_ast(self, ast):
self.print_pyexpr(ast)
elif isinstance(ast, dict):
self.print_dict(ast)
elif isinstance(ast, (str, unicode)):
elif isinstance(ast, str):
self.print_string(ast)
elif isinstance(ast, (int, long, bool)) or ast is None:
elif isinstance(ast, (bytes, bytearray)):
self.print_bytes(ast)
elif isinstance(ast, (int, bool)) or ast is None:
self.print_other(ast)
elif inspect.isclass(ast):
self.print_class(ast)
Expand Down Expand Up @@ -135,7 +135,7 @@ def should_print_key(self, ast, key):
ast.col_offset = 0 # TODO maybe make this match?
elif key == 'name' and type(ast.name) == tuple:
name = ast.name[0]
if isinstance(name, unicode):
if isinstance(name, str):
name = name.encode('utf-8')
ast.name = (name.split(b'/')[-1], 0, 0)
elif key == 'location' and type(ast.location) == tuple:
Expand Down Expand Up @@ -232,27 +232,50 @@ def print_class(self, ast):
def print_string(self, ast):
# prints the representation of a string. If there are newlines in this string,
# it will print it as a docstring.
if '\n' in ast:
astlist = ast.split('\n')
self.p('"""')
self.p(self.escape_string(astlist.pop(0)))
for i, item in enumerate(astlist):
self.p('\n')
self.p(self.escape_string(item))
self.p('"""')
self.ind()

else:
self.p(repr(ast))

def print_bytes(self, ast):
# prints the representation of a bytes object. If there are newlines in this string,
# it will print it as a docstring.
is_bytearray = isinstance(ast, bytearray)

if b'\n' in ast:
astlist = ast.split(b'\n')
if isinstance(ast, unicode):
self.p('u')
if is_bytearray:
self.p('bytearray(')
self.p('b')
self.p('"""')
self.p(self.escape_string(astlist.pop(0)))
for i, item in enumerate(astlist):
self.p('\n')
self.p(self.escape_string(item))
self.p('"""')
if is_bytearray:
self.p(')')
self.ind()

else:
self.p(repr(ast))

def escape_string(self, string):
# essentially the representation of a string without the surrounding quotes
if isinstance(string, unicode):
return repr(string)[2:-1]
elif isinstance(string, str):
if isinstance(string, str):
return repr(string)[1:-1]
elif isinstance(string, bytes):
return repr(string)[2:-1]
elif isinstance(string, bytearray):
return repr(bytes(string))[2:-1]
else:
return string

Expand All @@ -266,10 +289,10 @@ def ind(self, diff_indent=0, ast=None):
# shouldn't indent in case there's only one or zero objects in this object to print
if ast is None or len(ast) > 1:
self.indent += diff_indent
self.p(u'\n' + self.indentation * self.indent)
self.p('\n' + self.indentation * self.indent)

def p(self, string):
# write the string to the stream
string = unicode(string)
string = str(string)
self.linenumber += string.count('\n')
self.out_file.write(string)
5 changes: 1 addition & 4 deletions decompiler/atldecompiler.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@


from __future__ import unicode_literals
from util import DecompilerBase, WordConcatenator, Dispatcher
from .util import DecompilerBase, WordConcatenator, Dispatcher

import renpy

Expand Down
7 changes: 3 additions & 4 deletions decompiler/sl2decompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import unicode_literals
import sys
from operator import itemgetter

from util import DecompilerBase, First, reconstruct_paraminfo, \
reconstruct_arginfo, split_logical_lines, Dispatcher
from .util import DecompilerBase, First, reconstruct_paraminfo, \
reconstruct_arginfo, split_logical_lines, Dispatcher

import atldecompiler
from . import atldecompiler

from renpy import ui, sl2
from renpy.ast import PyExpr
Expand Down
3 changes: 1 addition & 2 deletions decompiler/testcasedecompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import unicode_literals
from util import DecompilerBase, split_logical_lines, Dispatcher, string_escape
from .util import DecompilerBase, split_logical_lines, Dispatcher, string_escape
from renpy.test import testast

# Main API
Expand Down
4 changes: 2 additions & 2 deletions decompiler/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from util import say_get_code
from .util import say_get_code
import renpy

import hashlib
import re
from copy import copy

class Translator(object):
class Translator:
def __init__(self, language, saving_translations=False):
self.language = language
self.saving_translations = saving_translations
Expand Down
23 changes: 11 additions & 12 deletions decompiler/util.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
from __future__ import unicode_literals
import sys
import re
from StringIO import StringIO
from io import StringIO
from contextlib import contextmanager

class OptionBase(object):
class OptionBase:
def __init__(self, indentation=" ", printlock=None):
self.indentation = indentation
self.printlock = printlock

class DecompilerBase(object):
class DecompilerBase:
def __init__(self, out_file=None, options=OptionBase()):
# the file object that the decompiler outputs to
self.out_file = out_file or sys.stdout
Expand Down Expand Up @@ -62,7 +61,7 @@ def write(self, string):
"""
Shorthand method for writing `string` to the file
"""
string = unicode(string)
string = str(string)
self.linenumber += string.count('\n')
self.skip_indent_until_write = False
self.out_file.write(string)
Expand Down Expand Up @@ -105,7 +104,7 @@ def rollback_state(self, state):
def advance_to_line(self, linenumber):
# If there was anything that we wanted to do as soon as we found a blank line,
# try to do it now.
self.blank_line_queue = filter(lambda m: m(linenumber), self.blank_line_queue)
self.blank_line_queue = [m for m in self.blank_line_queue if m(linenumber)]
if self.linenumber < linenumber:
# Stop one line short, since the call to indent() will advance the last line.
# Note that if self.linenumber == linenumber - 1, this will write the empty string.
Expand Down Expand Up @@ -178,7 +177,7 @@ def print_unknown(self, ast):
def print_node(self, ast):
raise NotImplementedError()

class First(object):
class First:
# An often used pattern is that on the first item
# of a loop something special has to be done. This class
# provides an easy object which on the first access
Expand Down Expand Up @@ -298,7 +297,7 @@ def simple_expression_guard(s):
# Some things we deal with are supposed to be parsed by
# ren'py's Lexer.simple_expression but actually cannot
# be parsed by it. figure out if this is the case
# a slightly more naive approach woudl be to check
# a slightly more naive approach would be to check
# for spaces in it and surround it with () if necessary
# but we're not naive
s = s.strip()
Expand All @@ -311,7 +310,7 @@ def simple_expression_guard(s):
def split_logical_lines(s):
return Lexer(s).split_logical_lines()

class Lexer(object):
class Lexer:
# special lexer for simple_expressions the ren'py way
# false negatives aren't dangerous. but false positives are
def __init__(self, string):
Expand Down Expand Up @@ -483,18 +482,18 @@ def __init__(self, needs_space, reorderable=False):
self.reorderable = reorderable

def append(self, *args):
self.words.extend(filter(None, args))
self.words.extend(i for i in args if i)

def join(self):
if not self.words:
return ''
if self.reorderable and self.words[-1][-1] == ' ':
for i in xrange(len(self.words) - 1, -1, -1):
for i in range(len(self.words) - 1, -1, -1):
if self.words[i][-1] != ' ':
self.words.append(self.words.pop(i))
break
last_word = self.words[-1]
self.words = map(lambda x: x[:-1] if x[-1] == ' ' else x, self.words[:-1])
self.words = [x[:-1] if x[-1] == ' ' else x for x in self.words[:-1]]
self.words.append(last_word)
rv = (' ' if self.needs_space else '') + ' '.join(self.words)
self.needs_space = rv[-1] != ' '
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
from setuptools import setup

def readme():
Expand Down
Loading

0 comments on commit a2d1343

Please sign in to comment.