summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Xu <alex_y_xu@yahoo.ca>2014-04-06 13:19:51 -0400
committerAlex Xu <alex_y_xu@yahoo.ca>2014-04-06 13:19:51 -0400
commitdc1c0b327a5427057e4268be74da8b2e46f0f98e (patch)
tree2a668bdac8a0c8c968c097b775c3cdb90c7cba19
parent3332be343ea24925795b7552155a8af2cea6c8d5 (diff)
downloadhtml5ks-dc1c0b327a5427057e4268be74da8b2e46f0f98e.tar.xz
html5ks-dc1c0b327a5427057e4268be74da8b2e46f0f98e.zip
reimplement imachine
-rwxr-xr-xast2json/imachine2json.py4
-rw-r--r--ast2json/renpy/__init__.py272
-rw-r--r--ast2json/renpy/ast.py1023
-rw-r--r--ast2json/renpy/atl.py179
-rw-r--r--ast2json/renpy/game.py259
-rw-r--r--ast2json/renpy/object.py120
-rw-r--r--ast2json/renpy/parser.py1842
-rw-r--r--ast2json/renpy/statements.py686
-rwxr-xr-xast2json/rpyc2json.py6
-rw-r--r--www/js/imachine.js99
-rw-r--r--www/js/menu.js23
11 files changed, 3157 insertions, 1356 deletions
diff --git a/ast2json/imachine2json.py b/ast2json/imachine2json.py
index db1878c..e865a73 100755
--- a/ast2json/imachine2json.py
+++ b/ast2json/imachine2json.py
@@ -8,9 +8,9 @@ def imachine2json(ast):
if ast[0]['_type'] != 'Label':
raise TypeError('obj does not start with Label, wrong file?')
for label in ast:
- if label['parameters'] is not None or label['hide']:
+ if label['parameters'] is not None:
raise NotImplementedError()
- ret[label['name']] = label
+ ret[label['name']] = label['block']
return ret
with open(sys.argv[1], 'r') as f:
diff --git a/ast2json/renpy/__init__.py b/ast2json/renpy/__init__.py
index f2bd463..a4190b1 100644
--- a/ast2json/renpy/__init__.py
+++ b/ast2json/renpy/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
+# Copyright 2004-2010 PyTom <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
@@ -22,250 +22,110 @@
# This file ensures that renpy packages will be imported in the right
# order.
-import sys
-import os
+# Some version numbers and things.
-# Version numbers.
-try:
- from renpy.vc_version import vc_version; vc_version
-except ImportError:
- vc_version = 0
-
-# The tuple giving the version. This needs to be updated when
-# we bump the version.
-#
-# Be sure to change script_version in launcher/script_version.rpy.
-# Be sure to change config.version in tutorial/game/options.rpy.
-version_tuple = (6, 15, 4, vc_version)
-
-# A verbose string computed from that version.
-version = "Ren'Py " + ".".join(str(i) for i in version_tuple)
-
-# Other versions.
+# ***** ***** ***** ***** ***** ***** **** ***** ***** ***** *****
+# Be sure to change script_version in launcher/script_version.rpy, too!
+# Also check to see if we have to update renpy.py.
+version = "Ren'Py 6.10.2e"
script_version = 5003000
savegame_suffix = "-LT1.save"
-bytecode_version = 1
-
-# True if this is the first time we've started - even including
-# utter restarts.
-first_utter_start = True
-
-def setup_modulefinder(modulefinder):
- import _renpy
-
- libexec = os.path.dirname(_renpy.__file__)
-
- for i in [ "display", "gl", "angle", "text" ]:
-
- displaypath = os.path.join(libexec, "renpy", i)
-
- if os.path.exists(displaypath):
- modulefinder.AddPackagePath('renpy.' + i, displaypath)
-
-def import_cython():
- """
- Never called, but necessary to ensure that modulefinder will properly
- grab the various cython modules.
- """
-
- import renpy.arguments #@UnresolvedImport
-
- import renpy.display.accelerator #@UnresolvedImport
- import renpy.display.render #@UnresolvedImport
- import renpy.gl.gldraw #@UnresolvedImport
- import renpy.gl.glenviron_fixed #@UnresolvedImport
- import renpy.gl.glenviron_limited #@UnresolvedImport
- import renpy.gl.glenviron_shader #@UnresolvedImport
- import renpy.gl.glrtt_copy #@UnresolvedImport
- import renpy.gl.glrtt_fbo #@UnresolvedImport
- import renpy.gl.gltexture #@UnresolvedImport
-
- import renpy.angle.gldraw #@UnresolvedImport
- import renpy.angle.glenviron_shader #@UnresolvedImport
- import renpy.angle.glrtt_copy #@UnresolvedImport
- import renpy.angle.glrtt_fbo #@UnresolvedImport
- import renpy.angle.gltexture #@UnresolvedImport
-
-
def import_all():
-
- import renpy.display #@UnresolvedImport
-
- # Adds in the Ren'Py loader.
- import renpy.loader #@UnresolvedImport
-
- import renpy.ast #@UnresolvedImport
- import renpy.atl #@UnresolvedImport
- import renpy.curry #@UnresolvedImport
- import renpy.easy #@UnresolvedImport
- import renpy.execution #@UnresolvedImport
- import renpy.loadsave #@UnresolvedImport
- import renpy.parser #@UnresolvedImport
- import renpy.python #@UnresolvedImport
- import renpy.script #@UnresolvedImport
- import renpy.statements #@UnresolvedImport
- import renpy.style #@UnresolvedImport
- import renpy.substitutions #@UnresolvedImport
- import renpy.translation #@UnresolvedImport
-
- import renpy.display.presplash #@UnresolvedImport
- import renpy.display.pgrender #@UnresolvedImport
- import renpy.display.scale #@UnresolvedImport
- import renpy.display.module #@UnresolvedImport
+ import renpy.game
- def update_path(package):
- """
- Update the __path__ of package, to import binary modules from a libexec
- directory.
- """
-
- name = package.__name__.split(".")
-
- import _renpy #@UnresolvedImport
- libexec = os.path.dirname(_renpy.__file__)
- package.__path__.append(os.path.join(libexec, *name))
-
- # Also find encodings, to deal with the way py2exe lays things out.
- import encodings
- libexec = os.path.dirname(encodings.__path__[0])
- package.__path__.append(os.path.join(libexec, *name))
+ # Should probably be early, as we will add it as a base to serialized things.
+ import renpy.object
- update_path(renpy.display)
-
- import renpy.display.render # Most display stuff depends on this. @UnresolvedImport
- import renpy.display.core # object @UnresolvedImport
-
- import renpy.text #@UnresolvedImport
- update_path(renpy.text)
-
- import renpy.text.ftfont #@UnresolvedImport
- import renpy.text.font #@UnresolvedImport
- import renpy.text.textsupport #@UnresolvedImport
- import renpy.text.texwrap #@UnresolvedImport
- import renpy.text.text #@UnresolvedImport
- import renpy.text.extras #@UnresolvedImport
-
- sys.modules['renpy.display.text'] = renpy.text.text
-
- import renpy.gl #@UnresolvedImport
- update_path(renpy.gl)
-
- import renpy.angle #@UnresolvedImport
- update_path(renpy.angle)
-
- import renpy.display.layout # core @UnresolvedImport
- import renpy.display.motion # layout @UnresolvedImport
- import renpy.display.behavior # layout @UnresolvedImport
- import renpy.display.transition # core, layout @UnresolvedImport
- import renpy.display.movetransition # core @UnresolvedImport
- import renpy.display.im #@UnresolvedImport
- import renpy.display.imagelike #@UnresolvedImport
- import renpy.display.image # core, behavior, im, imagelike @UnresolvedImport
- import renpy.display.video #@UnresolvedImport
- import renpy.display.focus #@UnresolvedImport
- import renpy.display.anim #@UnresolvedImport
- import renpy.display.particle #@UnresolvedImport
- import renpy.display.joystick #@UnresolvedImport
- import renpy.display.minigame #@UnresolvedImport
- import renpy.display.screen #@UnresolvedImport
- import renpy.display.dragdrop #@UnresolvedImport
- import renpy.display.imagemap #@UnresolvedImport
- import renpy.display.predict #@UnresolvedImport
+ # Adds in the Ren'Py loader.
+ import renpy.loader
+
+ import renpy.ast
+ import renpy.atl
+ import renpy.curry
+ import renpy.easy
+ import renpy.execution
+ import renpy.loadsave
+ import renpy.parser
+ import renpy.python # object
+ import renpy.remote
+ import renpy.script
+ import renpy.statements
+ import renpy.style
+
+ import renpy.display
+ import renpy.display.presplash
+ import renpy.display.iliad # Must be before scale and pgrender.
+ import renpy.display.pgrender
+ import renpy.display.scale # Must be before module.
+ import renpy.display.module
+ import renpy.display.render # Most display stuff depends on this.
+ import renpy.display.core # object
+ import renpy.display.font
+ import renpy.display.text # core, font
+ import renpy.display.layout # core
+ import renpy.display.motion # layout
+ import renpy.display.behavior # layout
+ import renpy.display.transition # core, layout
+ import renpy.display.im
+ import renpy.display.image # core, behavior, im
+ import renpy.display.video
+ import renpy.display.focus
+ import renpy.display.anim
+ import renpy.display.particle
+ import renpy.display.joystick
+ import renpy.display.minigame
+ import renpy.display.error
- import renpy.display.error #@UnresolvedImport
-
# Note: For windows to work, renpy.audio.audio needs to be after
# renpy.display.module.
- import renpy.audio.audio #@UnresolvedImport
- import renpy.audio.music #@UnresolvedImport
- import renpy.audio.sound #@UnresolvedImport
-
- import renpy.ui #@UnresolvedImport
- import renpy.screenlang #@UnresolvedImport
-
- import renpy.lint #@UnresolvedImport
- import renpy.warp #@UnresolvedImport
-
- import renpy.editor #@UnresolvedImport
- import renpy.exports #@UnresolvedImport
- import renpy.character # depends on exports. @UnresolvedImport
-
- import renpy.dump #@UnresolvedImport
+ import renpy.audio.audio
+ import renpy.audio.music
+ import renpy.audio.sound
- import renpy.config # depends on lots. @UnresolvedImport
- import renpy.minstore # depends on lots. @UnresolvedImport
- import renpy.defaultstore # depends on everything. @UnresolvedImport
- import renpy.main #@UnresolvedImport
+ import renpy.ui
- # Create the store.
- renpy.python.create_store("store")
+ import renpy.lint
+ import renpy.warp
- # Import the contents of renpy.defaultstore into renpy.store, and set
- # up an alias as we do.
- renpy.store = sys.modules['store']
- sys.modules['renpy.store'] = sys.modules['store']
+ import renpy.exports
+ import renpy.character # depends on exports.
- import subprocess
- sys.modules['renpy.subprocess'] = subprocess
-
- for k, v in renpy.defaultstore.__dict__.items():
- renpy.store.__dict__.setdefault(k, v)
+ import renpy.config # depends on lots.
+ import renpy.store # depends on everything.
+ import renpy.main
# Import everything into renpy.exports, provided it isn't
# already there.
for k, v in globals().items():
vars(renpy.exports).setdefault(k, v)
-# Fool the analyzer.
-if False:
- import renpy.defaultstore as store
-
# This reloads all modules.
def reload_all():
- import renpy #@UnresolvedImport
-
+ import renpy
+
# Shut down the cache thread.
renpy.display.im.cache.quit()
+ # Cleans out the RenpyImporter.
+ import sys
+ sys.meta_path.pop()
+
blacklist = [ "renpy",
"renpy.bootstrap",
"renpy.display",
+ "renpy.display.iliad",
"renpy.display.pgrender",
"renpy.display.scale" ]
for i in list(sys.modules.keys()):
if i.startswith("renpy") and i not in blacklist:
del sys.modules[i]
-
- if i.startswith("store"):
- del sys.modules[i]
import gc
gc.collect()
- renpy.display.draw = None
-
import_all()
-# Information about the platform we're running on. We break the platforms
-# up into 4 groups - windows-like, mac-like, linux-like, and android-like.
-windows = False
-macintosh = False
-linux = False
-android = False
-
-import platform
-
-if platform.win32_ver()[0]:
- windows = True
-elif platform.mac_ver()[0]:
- macintosh = True
-else:
- linux = True
-
-# The android init code in renpy.py will set linux=False and android=True.
-
-
diff --git a/ast2json/renpy/ast.py b/ast2json/renpy/ast.py
index 6fbd58f..b5d2200 100644
--- a/ast2json/renpy/ast.py
+++ b/ast2json/renpy/ast.py
@@ -1,4 +1,4 @@
-# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
+# Copyright 2004-2010 PyTom <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
@@ -27,20 +27,13 @@
# updating.
import renpy
-
import re
import time
-import hashlib
-import collections
-
-def next_node(n):
- """
- Indicates the next node that should be executed. When a statement
- can crash, this should be set as early as possible, so that ignore
- can bring us there.
- """
- renpy.game.context().next_node = n
+# Called to set the state of a Node, when necessary.
+def setstate(node, state):
+ for k, v in state[1].items():
+ setattr(node, k, v)
class ParameterInfo(object):
"""
@@ -82,14 +75,14 @@ class ArgumentInfo(object):
def __newobj__(cls, *args):
- return cls.__new__(cls, *args)
+ return cls.__new__(cls, *args)
# This represents a string containing python code.
class PyExpr(str):
- __slots__ = [
+ __slots__ = [
'filename',
- 'linenumber',
+ 'linenumber'
]
def __new__(cls, s, filename, linenumber):
@@ -100,7 +93,8 @@ class PyExpr(str):
def __getnewargs__(self):
return (str(self), self.filename, self.linenumber) # E1101
-
+
+
class PyCode(object):
__slots__ = [
@@ -108,24 +102,19 @@ class PyCode(object):
'location',
'mode',
'bytecode',
- 'hash',
]
+ # All PyCodes known to the system.
+ extent = [ ]
+
def __getstate__(self):
return (1, self.source, self.location, self.mode)
def __setstate__(self, state):
(_, self.source, self.location, self.mode) = state
self.bytecode = None
-
- if renpy.game.script.record_pycode:
- renpy.game.script.all_pycode.append(self)
-
+
def __init__(self, source, loc=('<none>', 1), mode='exec'):
-
- if isinstance(source, PyExpr):
- loc = (source.filename, source.linenumber, source)
-
# The source code.
self.source = source
@@ -137,27 +126,7 @@ class PyCode(object):
# This will be initialized later on, after we are serialized.
self.bytecode = None
- if renpy.game.script.record_pycode:
- renpy.game.script.all_pycode.append(self)
-
- self.hash = None
-
- def get_hash(self):
- try:
- if self.hash is not None:
- return self.hash
- except:
- pass
-
- code = self.source
- if isinstance(code, renpy.python.ast.AST): #@UndefinedVariable
- code = renpy.python.ast.dump(code) #@UndefinedVariable
-
- self.hash = chr(renpy.bytecode_version) + hashlib.md5(repr(self.location) + code.encode("utf-8")).digest()
- return self.hash
-
-
-def chain_block(block, next): #@ReservedAssignment
+def chain_block(block, next):
"""
This is called to chain together all of the nodes in a block. Node
n is chained with node n+1, while the last node is chained with
@@ -183,13 +152,13 @@ class Scry(object):
def __getattr__(self, name):
return None
- def __next__(self): #@ReservedAssignment
+ def __next__(self):
if self._next is None:
return None
else:
return self._next.scry()
-
+
class Node(object):
"""
A node in the abstract syntax tree of the program.
@@ -199,7 +168,7 @@ class Node(object):
@ivar filename: The filename where this node comes from.
@ivar linenumber: The line number of the line on which this node is defined.
"""
-
+
__slots__ = [
'name',
'filename',
@@ -207,19 +176,6 @@ class Node(object):
'next',
]
- # True if this node is translatable, false otherwise. (This can be set on
- # the class or the instance.)
- translatable = False
-
- # Called to set the state of a Node, when necessary.
- def __setstate__(self, state):
- for k, v in state[1].items():
- try:
- setattr(self, k, v)
- except AttributeError:
- pass
-
-
def __init__(self, loc):
"""
Initializes this Node object.
@@ -231,7 +187,7 @@ class Node(object):
self.filename, self.linenumber = loc
self.name = None
self.next = None
-
+
def diff_info(self):
"""
Returns a tuple of diff info about ourself. This is used to
@@ -260,14 +216,14 @@ class Node(object):
"""
return None
-
- def chain(self, next): #@ReservedAssignment
+
+ def chain(self, next):
"""
This is called with the Node node that should be followed after
executing this node, and all nodes that this node
executes. (For example, if this node is a block label, the
next is the node that should be executed after all nodes in
- the block.)
+ the block.)
"""
self.next = next
@@ -275,22 +231,20 @@ class Node(object):
def execute(self):
"""
Causes this node to execute, and any action it entails to be
- performed. The node should call next_node with the node to
- be executed after this one.
+ performed. The node is then responsible for returning the node
+ that should be executed after this one, or None to end the
+ program or init block.
"""
assert False, "Node subclass forgot to define execute."
- def early_execute(self):
- """
- Called when the module is loaded.
- """
-
- def predict(self):
+ def predict(self, callback):
"""
- This is called to predictively load images from this node. It
- should cause renpy.display.predict.image and
- renpy.display.predict.screen to be called as necessary.
+ This is called to predictively load images from this node. The
+ callback needs to be passed into the predict method of any
+ images this ast node will probably load, and the method should
+ return a list containing the nodes that this node will
+ probably execute next.
"""
if self.__next__:
@@ -298,6 +252,14 @@ class Node(object):
else:
return [ ]
+ def get_pycode(self):
+ """
+ Returns a list of PyCode objects associated with this Node,
+ or None if no objects are associated with it.
+ """
+
+ return [ ]
+
def scry(self):
"""
Called to return an object with some general, user-definable information
@@ -308,30 +270,6 @@ class Node(object):
rv._next = self.__next__ # W0201
return rv
- def restructure(self, callback):
- """
- Called to restructure the AST.
-
- When this method is called, callback is called once for each child
- block of the node. The block, a list, can be updated by the callback
- using slice assignment to the list.
- """
-
- # Does nothing for nodes that do not contain child blocks.
- return
-
- def get_code(self, dialogue_filter=None):
- """
- Returns the canonical form of the code corresponding to this statement.
- This only needs to be defined if the statement is translatable.
-
- `filter`
- If present, a filter that should be applied to human-readable
- text in the statement.
- """
-
- raise Exception("Not Implemented")
-
def say_menu_with(expression, callback):
"""
This handles the with clause of a say or menu statement.
@@ -350,7 +288,7 @@ def say_menu_with(expression, callback):
if renpy.game.preferences.transitions:
# renpy.game.interface.set_transition(what)
callback(what)
-
+
class Say(Node):
__slots__ = [
@@ -359,18 +297,12 @@ class Say(Node):
'what',
'with_',
'interact',
- 'attributes',
]
def diff_info(self):
return (Say, self.who, self.what)
- def __setstate__(self, state):
- self.attributes = None
- self.interact = True
- Node.__setstate__(self, state)
-
- def __init__(self, loc, who, what, with_, interact=True, attributes=None):
+ def __init__(self, loc, who, what, with_, interact=True):
super(Say, self).__init__(loc)
@@ -382,131 +314,68 @@ class Say(Node):
else:
self.who_fast = False
else:
- self.who = None
+ self.who = None
self.who_fast = False
-
+
self.what = what
self.with_ = with_
self.interact = interact
- # A tuple of attributes that are applied to the character that's
- # speaking, or None to disable this behavior.
- self.attributes = attributes
-
- def get_code(self, dialogue_filter=None):
- rv = [ ]
-
- if self.who:
- rv.append(self.who)
-
- if self.attributes is not None:
- rv.extend(self.attributes)
-
- what = self.what
- if dialogue_filter is not None:
- what = dialogue_filter(what)
-
- rv.append(renpy.translation.encode_say_string(what))
-
- if not self.interact:
- rv.append("nointeract")
-
- if self.with_:
- rv.append("with")
- rv.append(self.with_)
-
- return " ".join(rv)
-
def execute(self):
- next_node(self.__next__)
-
- try:
-
- renpy.exports.say_attributes = self.attributes
-
- if self.who is not None:
- if self.who_fast:
- who = getattr(renpy.store, self.who, None)
- if who is None:
- raise Exception("Sayer '%s' is not defined." % self.who.encode("utf-8"))
- else:
- who = renpy.python.py_eval(self.who)
+ if self.who is not None:
+ if self.who_fast:
+ who = getattr(renpy.store, self.who, None)
+ if who is None:
+ raise Exception("Sayer '%s' is not defined." % self.who.encode("utf-8"))
else:
- who = None
-
- if not (
- (who is None) or
- isinstance(who, collections.Callable) or
- isinstance(who, str) ):
-
- raise Exception("Sayer %s is not a function or string." % self.who.encode("utf-8"))
-
- what = self.what
- if renpy.config.say_menu_text_filter:
- what = renpy.config.say_menu_text_filter(what) # E1102
-
- if getattr(who, "record_say", True):
- renpy.store._last_say_who = self.who
- renpy.store._last_say_what = what
-
- say_menu_with(self.with_, renpy.game.interface.set_transition)
- renpy.exports.say(who, what, interact=self.interact)
-
- finally:
- renpy.exports.say_attributes = None
-
+ who = renpy.python.py_eval(self.who)
+ else:
+ who = None
- def predict(self):
+ what = self.what
+ if renpy.config.say_menu_text_filter:
+ what = renpy.config.say_menu_text_filter(what) # E1102
+
+ say_menu_with(self.with_, renpy.game.interface.set_transition)
+ renpy.exports.say(who, what, interact=getattr(self, 'interact', True))
- old_attributes = renpy.exports.say_attributes
+ if getattr(who, "record_say", True):
+ renpy.store._last_say_who = self.who
+ renpy.store._last_say_what = what
- try:
+ return self.__next__
- renpy.exports.say_attributes = self.attributes
+ def predict(self, callback):
- if self.who is not None:
- if self.who_fast:
- who = getattr(renpy.store, self.who)
- else:
- who = renpy.python.py_eval(self.who)
+ if self.who is not None:
+ if self.who_fast:
+ who = getattr(renpy.store, self.who)
else:
- who = None
-
- def predict_with(trans):
- renpy.display.predict.displayable(trans(old_widget=None, new_widget=None))
+ who = renpy.python.py_eval(self.who)
+ else:
+ who = None
- say_menu_with(self.with_, predict_with)
+ def predict_with(trans):
+ trans(old_widget=None, new_widget=None).predict(callback)
- what = self.what
- if renpy.config.say_menu_text_filter:
- what = renpy.config.say_menu_text_filter(what)
+ say_menu_with(self.with_, predict_with)
- renpy.exports.predict_say(who, what)
+ what = self.what
+ if renpy.config.say_menu_text_filter:
+ what = renpy.config.say_menu_text_filter(what)
- finally:
- renpy.exports.say_attributes = old_attributes
+ for i in renpy.exports.predict_say(who, what):
+ if i is not None:
+ i.predict(callback)
return [ self.__next__ ]
def scry(self):
rv = Node.scry(self)
-
- if self.who is not None:
- if self.who_fast:
- who = getattr(renpy.store, self.who)
- else:
- who = renpy.python.py_eval(self.who)
- else:
- who = None
-
- if self.interact:
- renpy.exports.scry_say(who, rv)
- else:
- rv.interacts = False
-
+ rv.interacts = True # W0201
return rv
-
+
# Copy the descriptor.
setattr(Say, "with", Say.with_) # E1101
@@ -533,87 +402,14 @@ class Init(Node):
# We handle chaining specially. We want to chain together the nodes in
# the block, but we want that chain to end in None, and we also want
# this node to just continue on to the next node in normal execution.
- def chain(self, next): #@ReservedAssignment
+ def chain(self, next):
self.next = next
chain_block(self.block, None)
def execute(self):
- next_node(self.__next__)
-
- def restructure(self, callback):
- callback(self.block)
-
-def apply_arguments(params, args, kwargs):
- """
- Applies arguments to parameters to update scope.
-
- `scope`
- A dict.
-
- `params`
- The parameters object.
-
- `args`, `kwargs`
- Positional and keyword arguments.
- """
-
- values = { }
- rv = { }
-
- if args is None:
- args = ()
-
- if kwargs is None:
- kwargs = { }
-
- if params is None:
- if args or kwargs:
- raise Exception("Arguments supplied, but parameter list not present")
- else:
- return rv
-
- for name, value in zip(params.positional, args):
- if name in values:
- raise Exception("Parameter %s has two values." % name)
-
- values[name] = value
-
- extrapos = tuple(args[len(params.positional):])
-
- for name, value in kwargs.items():
- if name in values:
- raise Exception("Parameter %s has two values." % name)
-
- values[name] = value
-
- for name, default in params.parameters:
-
- if name not in values:
- if default is None:
- raise Exception("Required parameter %s has no value." % name)
- else:
- rv[name] = renpy.python.py_eval(default)
-
- else:
- rv[name] = values[name]
- del values[name]
-
- # Now, values has the left-over keyword arguments, and extrapos
- # has the left-over positional arguments.
-
- if params.extrapos:
- rv[params.extrapos] = extrapos
- elif extrapos:
- raise Exception("Too many arguments in call (expected %d, got %d)." % (len(params.positional), len(args)))
-
- if params.extrakw:
- rv[params.extrakw] = values
- else:
- if values:
- raise Exception("Unknown keyword arguments: %s" % ( ", ".join(list(values.keys()))))
-
- return rv
+ return self.__next__
+
class Label(Node):
@@ -621,15 +417,13 @@ class Label(Node):
'name',
'parameters',
'block',
- 'hide',
]
def __setstate__(self, state):
self.parameters = None
- self.hide = False
- Node.__setstate__(self, state)
-
- def __init__(self, loc, name, block, parameters, hide=False):
+ setstate(self, state)
+
+ def __init__(self, loc, name, block, parameters):
"""
Constructs a new Label node.
@@ -643,85 +437,126 @@ class Label(Node):
self.name = name
self.block = block
self.parameters = parameters
- self.hide = hide
def diff_info(self):
return (Label, self.name)
def get_children(self):
- return self.block
+ return self.block
- def chain(self, next): #@ReservedAssignment
+ def chain(self, next):
if self.block:
self.next = self.block[0]
chain_block(self.block, next)
else:
self.next = next
-
+
def execute(self):
- next_node(self.__next__)
-
renpy.game.context().mark_seen()
+
+ args = renpy.store._args
+ kwargs = renpy.store._kwargs
+
+ if self.parameters is None:
+ if (args is not None) or (kwargs is not None):
+ raise Exception("Arguments supplied, but label does not take parameters.")
+ else:
+ if renpy.config.label_callback:
+ renpy.config.label_callback(self.name, renpy.game.context().last_abnormal)
+
+ return self.__next__
+ else:
+ if args is None:
+ args = ()
+
+ if kwargs is None:
+ kwargs = { }
+
+ values = { }
+ params = self.parameters
+
+ for name, value in zip(params.positional, args):
+ if name in values:
+ raise Exception("Parameter %s has two values." % name)
+
+ values[name] = value
- values = apply_arguments(self.parameters, renpy.store._args, renpy.store._kwargs)
+ extrapos = tuple(args[len(params.positional):])
- for k, v in values.items():
- renpy.exports.dynamic(k)
- setattr(renpy.store, k, v)
+ for name, value in kwargs.items():
+ if name in values:
+ raise Exception("Parameter %s has two values." % name)
+
+ values[name] = value
+
+ for name, default in params.parameters:
+
+ if name not in values:
+ if default is None:
+ raise Exception("Required parameter %s has no value." % name)
+ else:
+ values[name] = renpy.python.py_eval(default)
+
+
+ renpy.exports.dynamic(name)
+ setattr(renpy.store, name, values[name])
+ del values[name]
+
+ # Now, values has the left-over keyword arguments, and extrapos
+ # has the left-over positional arguments.
+
+ if params.extrapos:
+ renpy.exports.dynamic(params.extrapos)
+ setattr(renpy.store, params.extrapos, extrapos)
+ else:
+ if extrapos:
+ raise Exception("Too many arguments in call (expected %d, got %d)." % (len(params.positional), len(args)))
+
+ if params.extrakw:
+ renpy.exports.dynamic(params.extrakw)
+ setattr(renpy.store, params.extrakw, renpy.python.RevertableDict(values))
+ else:
+ if values:
+ raise Exception("Unknown keyword arguments: %s" % ( ", ".join(list(values.keys()))))
renpy.store._args = None
renpy.store._kwargs = None
if renpy.config.label_callback:
renpy.config.label_callback(self.name, renpy.game.context().last_abnormal)
-
- def restructure(self, callback):
- callback(self.block)
-
+
+ return self.__next__
class Python(Node):
__slots__ = [
'hide',
'code',
- 'store',
]
- def __setstate__(self, state):
- self.store = "store"
- super(Python, self).__setstate__(state)
-
- def __init__(self, loc, python_code, hide=False, store="store"):
+ def __init__(self, loc, python_code, hide=False):
"""
@param code: A PyCode object.
@param hide: If True, the code will be executed with its
own local dictionary.
"""
-
+
super(Python, self).__init__(loc)
self.hide = hide
self.code = PyCode(python_code, loc=loc, mode='exec')
- self.store = store
+
+ def get_pycode(self):
+ return [ self.code ]
def diff_info(self):
return (Python, self.code.source)
- def early_execute(self):
- renpy.python.create_store(self.store)
-
def execute(self):
- next_node(self.__next__)
-
- try:
- renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
- finally:
-
- if not renpy.game.context().init_phase:
- for i in renpy.config.python_callbacks:
- i()
+ renpy.python.py_exec_bytecode(self.code.bytecode, self.hide)
+ return self.__next__
def scry(self):
rv = Node.scry(self)
@@ -733,38 +568,32 @@ class EarlyPython(Node):
__slots__ = [
'hide',
'code',
- 'store',
]
- def __setstate__(self, state):
- self.store = "store"
- super(EarlyPython, self).__setstate__(state)
-
- def __init__(self, loc, python_code, hide=False, store="store"):
+ def __init__(self, loc, python_code, hide=False):
"""
@param code: A PyCode object.
@param hide: If True, the code will be executed with its
own local dictionary.
"""
-
+
super(EarlyPython, self).__init__(loc)
self.hide = hide
self.code = PyCode(python_code, loc=loc, mode='exec')
- self.store = store
+
+ def get_pycode(self):
+ return [ self.code ]
def diff_info(self):
return (EarlyPython, self.code.source)
def execute(self):
- next_node(self.__next__)
+ return self.__next__
def early_execute(self):
- renpy.python.create_store(self.store)
-
- if self.code.bytecode:
- renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
+ renpy.python.py_exec_bytecode(self.code.bytecode, self.hide)
class Image(Node):
@@ -783,7 +612,7 @@ class Image(Node):
"""
super(Image, self).__init__(loc)
-
+
self.imgname = name
if expr:
@@ -792,17 +621,21 @@ class Image(Node):
else:
self.code = None
self.atl = atl
-
- def diff_info(self):
+
+ def diff_info(self):
return (Image, tuple(self.imgname))
+ def get_pycode(self):
+ if self.code:
+ return [ self.code ]
+ else:
+ return [ ]
+
def execute(self):
# Note: We should always check that self.code is None before
# accessing self.atl, as self.atl may not always exist.
- next_node(self.__next__)
-
if self.code is not None:
img = renpy.python.py_eval_bytecode(self.code.bytecode)
else:
@@ -810,8 +643,10 @@ class Image(Node):
renpy.exports.image(self.imgname, img)
+ return self.__next__
+
class Transform(Node):
__slots__ = [
@@ -823,52 +658,52 @@ class Transform(Node):
'atl',
# The parameters associated with the transform, if any.
- 'parameters',
+ 'parameters',
]
default_parameters = ParameterInfo([ ], [ ], None, None)
-
+
def __init__(self, loc, name, atl=None, parameters=default_parameters):
super(Transform, self).__init__(loc)
-
+
self.varname = name
self.atl = atl
self.parameters = parameters
-
- def diff_info(self):
+
+ def diff_info(self):
return (Transform, self.varname)
def execute(self):
- next_node(self.__next__)
-
parameters = getattr(self, "parameters", None)
if parameters is None:
parameters = Transform.default_parameters
trans = renpy.display.motion.ATLTransform(self.atl, parameters=parameters)
- renpy.dump.transforms.append((self.varname, self.filename, self.linenumber))
+ renpy.exports.definitions[self.varname].append((self.filename, self.linenumber, "transform"))
setattr(renpy.store, self.varname, trans)
+
+ return self.__next__
-
-def predict_imspec(imspec, scene=False, atl=None):
+
+def predict_imspec(imspec, callback, scene=False):
"""
Call this to use the given callback to predict the image named
in imspec.
"""
if len(imspec) == 7:
- name, expression, tag, _at_list, layer, _zorder, _behind = imspec
+ name, expression, tag, at_list, layer, zorder, behind = imspec
elif len(imspec) == 6:
- name, expression, tag, _at_list, layer, _zorder = imspec
+ name, expression, tag, at_list, layer, zorder = imspec
elif len(imspec) == 3:
- name, _at_list, layer = imspec
-
-
+ name, at_list, layer = imspec
+
+
if expression:
try:
img = renpy.python.py_eval(expression)
@@ -877,7 +712,7 @@ def predict_imspec(imspec, scene=False, atl=None):
return
else:
- img = renpy.display.image.images.get(name, None)
+ img = renpy.exports.images.get(name, None)
if img is None:
return
@@ -886,37 +721,29 @@ def predict_imspec(imspec, scene=False, atl=None):
full_name = (tag,) + full_name[1:]
if scene:
- renpy.game.context().images.predict_scene(layer)
-
- renpy.game.context().images.predict_show(tag or name, layer)
-
- if atl is not None:
- try:
- img = renpy.display.motion.ATLTransform(atl, child=img)
- except:
- import traceback
- traceback.print_exc()
-
- renpy.display.predict.displayable(img)
-
-
+ renpy.game.context().predict_info.images.predict_scene(layer)
+
+ renpy.game.context().predict_info.images.predict_show(tag or name, layer)
+
+ img.predict(callback)
+
def show_imspec(imspec, atl=None):
if len(imspec) == 7:
name, expression, tag, at_list, layer, zorder, behind = imspec
-
+
elif len(imspec) == 6:
name, expression, tag, at_list, layer, zorder = imspec
behind = [ ]
-
+
elif len(imspec) == 3:
name, at_list, layer = imspec
expression = None
tag = None
zorder = None
behind = [ ]
-
+
if zorder is not None:
zorder = renpy.python.py_eval(zorder)
else:
@@ -954,19 +781,20 @@ class Show(Node):
self.imspec = imspec
self.atl = atl
-
- def diff_info(self):
+
+ def diff_info(self):
return (Show, tuple(self.imspec[0]))
def execute(self):
- next_node(self.__next__)
show_imspec(self.imspec, atl=getattr(self, "atl", None))
- def predict(self):
- predict_imspec(self.imspec, atl=getattr(self, "atl", None))
- return [ self.__next__ ]
+ return self.__next__
+ def predict(self, callback):
+ predict_imspec(self.imspec, callback)
+ return [ self.__next__ ]
+
class Scene(Node):
@@ -989,7 +817,7 @@ class Scene(Node):
self.layer = layer
self.atl = atl
- def diff_info(self):
+ def diff_info(self):
if self.imspec:
data = tuple(self.imspec[0])
@@ -1000,21 +828,21 @@ class Scene(Node):
def execute(self):
- next_node(self.__next__)
-
renpy.config.scene(self.layer)
if self.imspec:
show_imspec(self.imspec, atl=getattr(self, "atl", None))
- def predict(self):
-
+ return self.__next__
+
+ def predict(self, callback):
+
if self.imspec:
- predict_imspec(self.imspec, atl=getattr(self, "atl", None), scene=True)
+ predict_imspec(self.imspec, callback, scene=True)
return [ self.__next__ ]
-
+
class Hide(Node):
__slots__ = [
@@ -1032,48 +860,46 @@ class Hide(Node):
self.imspec = imgspec
- def diff_info(self):
+ def diff_info(self):
return (Hide, tuple(self.imspec[0]))
- def predict(self):
+ def predict(self, callback):
if len(self.imspec) == 3:
- name, _at_list, layer = self.imspec
+ name, at_list, layer = self.imspec
+ expression = None
tag = None
- _expression = None
- _zorder = None
- _behind = None
+ zorder = 0
elif len(self.imspec) == 6:
- name, _expression, tag, _at_list, layer, _zorder = self.imspec
- _behind = None
+ name, expression, tag, at_list, layer, zorder = self.imspec
elif len(self.imspec) == 7:
- name, _expression, tag, _at_list, layer, _zorder, _behind = self.imspec
+ name, expression, tag, at_list, layer, zorder, behind = self.imspec
if tag is None:
tag = name[0]
+
+ renpy.game.context().predict_info.images.predict_hide(tag, layer)
- renpy.game.context().images.predict_hide(tag, layer)
-
- return [ self.__next__ ]
-
+ return [ ]
+
def execute(self):
- next_node(self.__next__)
-
if len(self.imspec) == 3:
- name, _at_list, layer = self.imspec
- _expression = None
+ name, at_list, layer = self.imspec
+ expression = None
tag = None
- _zorder = 0
+ zorder = 0
elif len(self.imspec) == 6:
- name, _expression, tag, _at_list, layer, _zorder = self.imspec
+ name, expression, tag, at_list, layer, zorder = self.imspec
elif len(self.imspec) == 7:
- name, _expression, tag, _at_list, layer, _zorder, _behind = self.imspec
-
+ name, expression, tag, at_list, layer, zorder, behind = self.imspec
+
renpy.config.hide(tag or name, layer)
+ return self.__next__
+
class With(Node):
__slots__ = [
@@ -1083,8 +909,8 @@ class With(Node):
def __setstate__(self, state):
self.paired = None
- Node.__setstate__(self, state)
-
+ setstate(self, state)
+
def __init__(self, loc, expr, paired=None):
"""
@param expr: An expression giving a transition or None.
@@ -1096,35 +922,34 @@ class With(Node):
def diff_info(self):
return (With, self.expr)
-
+
def execute(self):
- next_node(self.__next__)
-
trans = renpy.python.py_eval(self.expr)
if self.paired is not None:
paired = renpy.python.py_eval(self.paired)
else:
- paired = None
+ paired = None
renpy.exports.with_statement(trans, paired)
- def predict(self):
+ return self.__next__
+
+ def predict(self, callback):
try:
trans = renpy.python.py_eval(self.expr)
if trans:
- renpy.display.predict.displayable(trans(old_widget=None, new_widget=None))
-
+ trans(old_widget=None, new_widget=None).predict(callback)
except:
pass
-
+
return [ self.__next__ ]
-
-
+
+
class Call(Node):
__slots__ = [
@@ -1135,8 +960,8 @@ class Call(Node):
def __setstate__(self, state):
self.arguments = None
- Node.__setstate__(self, state)
-
+ setstate(self, state)
+
def __init__(self, loc, label, expression, arguments):
super(Call, self).__init__(loc)
@@ -1154,7 +979,6 @@ class Call(Node):
label = renpy.python.py_eval(label)
rv = renpy.game.context().call(label, return_site=self.next.name)
- next_node(rv)
renpy.game.context().abnormal = True
if self.arguments:
@@ -1187,8 +1011,12 @@ class Call(Node):
renpy.store._args = tuple(args)
renpy.store._kwargs = kwargs
-
- def predict(self):
+
+
+ return rv
+
+
+ def predict(self, callback):
if self.expression:
return [ ]
else:
@@ -1206,17 +1034,17 @@ class Return(Node):
def __setstate__(self, state):
self.expression = None
- Node.__setstate__(self, state)
-
+ setstate(self, state)
+
def __init__(self, loc, expression):
super(Return, self).__init__(loc)
self.expression = expression
-
+
def diff_info(self):
return (Return, )
# We don't care what the next node is.
- def chain(self, next): #@ReservedAssignment
+ def chain(self, next):
self.next = None
return
@@ -1228,10 +1056,10 @@ class Return(Node):
renpy.store._return = None
renpy.game.context().pop_dynamic()
+
+ return renpy.game.context().lookup_return(pop=True)
- next_node(renpy.game.context().lookup_return(pop=True))
-
- def predict(self):
+ def predict(self, callback):
site = renpy.game.context().lookup_return(pop=False)
if site:
return [ site ]
@@ -1243,7 +1071,7 @@ class Return(Node):
rv._next = None
return rv
-
+
class Menu(Node):
__slots__ = [
@@ -1252,7 +1080,7 @@ class Menu(Node):
'with_',
]
- def __init__(self, loc, items, set, with_): #@ReservedAssignment
+ def __init__(self, loc, items, set, with_):
super(Menu, self).__init__(loc)
self.items = items
@@ -1265,65 +1093,57 @@ class Menu(Node):
def get_children(self):
rv = [ ]
- for _label, _condition, block in self.items:
+ for label, condition, block in self.items:
if block:
rv.extend(block)
return rv
# Blocks of statements in a choice continue after the menu.
- def chain(self, next): #@ReservedAssignment
+ def chain(self, next):
self.next = next
- for (_label, _condition, block) in self.items:
+ for (label, condition, block) in self.items:
if block:
chain_block(block, next)
def execute(self):
- next_node(self.__next__)
-
choices = [ ]
- narration = [ ]
-
+
for i, (label, condition, block) in enumerate(self.items):
if renpy.config.say_menu_text_filter:
label = renpy.config.say_menu_text_filter(label)
if block is None:
- if renpy.config.narrator_menu and label:
- narration.append(label)
- else:
- choices.append((label, condition, None))
+ choices.append((label, condition, None))
else:
choices.append((label, condition, i))
- next_node(block[0])
-
- if narration:
- renpy.exports.say(None, "\n".join(narration), interact=False)
say_menu_with(self.with_, renpy.game.interface.set_transition)
choice = renpy.exports.menu(choices, self.set)
- if choice is not None:
- next_node(self.items[choice][2][0])
+ if choice is None:
+ return self.__next__
else:
- next_node(self.__next__)
-
+ return self.items[choice][2][0]
+
- def predict(self):
+ def predict(self, callback):
rv = [ ]
def predict_with(trans):
- renpy.display.predict.displayable(trans(old_widget=None, new_widget=None))
+ trans(old_widget=None, new_widget=None).predict(callback)
say_menu_with(self.with_, predict_with)
- renpy.store.predict_menu()
+ for i in renpy.store.predict_menu():
+ if i is not None:
+ i.predict(callback)
- for _label, _condition, block in self.items:
+ for label, condition, block in self.items:
if block:
rv.append(block[0])
@@ -1334,17 +1154,12 @@ class Menu(Node):
rv._next = None
rv.interacts = True
return rv
-
- def restructure(self, callback):
- for _label, _condition, block in self.items:
- if block is not None:
- callback(block)
-
+
setattr(Menu, "with", Menu.with_) # E1101
# Goto is considered harmful. So we decided to name it "jump"
-# instead.
+# instead.
class Jump(Node):
__slots__ = [
@@ -1362,7 +1177,7 @@ class Jump(Node):
return (Jump, self.target, self.expression)
# We don't care what our next node is.
- def chain(self, next): #@ReservedAssignment
+ def chain(self, next):
self.next = None
return
@@ -1374,10 +1189,9 @@ class Jump(Node):
rv = renpy.game.script.lookup(target)
renpy.game.context().abnormal = True
+ return rv
- next_node(rv)
-
- def predict(self):
+ def predict(self, callback):
if self.expression:
return [ ]
@@ -1390,7 +1204,7 @@ class Jump(Node):
rv._next = None
else:
rv._next = renpy.game.script.lookup(self.target)
-
+
return rv
@@ -1403,7 +1217,7 @@ class Pass(Node):
return (Pass,)
def execute(self):
- next_node(self.__next__)
+ return self.__next__
class While(Node):
@@ -1425,28 +1239,25 @@ class While(Node):
def get_children(self):
return self.block
- def chain(self, next): #@ReservedAssignment
+ def chain(self, next):
self.next = next
chain_block(self.block, self)
def execute(self):
- next_node(self.__next__)
-
if renpy.python.py_eval(self.condition):
- next_node(self.block[0])
+ return self.block[0]
+ else:
+ return self.__next__
- def predict(self):
+ def predict(self, callback):
return [ self.block[0], self.__next__ ]
-
+
def scry(self):
rv = Node.scry(self)
rv._next = None
return rv
- def restructure(self, callback):
- callback(self.block)
-
class If(Node):
__slots__ = [ 'entries' ]
@@ -1466,29 +1277,28 @@ class If(Node):
def get_children(self):
rv = [ ]
- for _condition, block in self.entries:
+ for condition, block in self.entries:
rv.extend(block)
return rv
- def chain(self, next): #@ReservedAssignment
+ def chain(self, next):
self.next = next
- for _condition, block in self.entries:
+ for condition, block in self.entries:
chain_block(block, next)
def execute(self):
- next_node(self.__next__)
-
for condition, block in self.entries:
if renpy.python.py_eval(condition):
- next_node(block[0])
- return
+ return block[0]
+
+ return self.__next__
- def predict(self):
+ def predict(self, callback):
- return [ block[0] for _condition, block in self.entries ] + \
+ return [ block[0] for condition, block in self.entries ] + \
[ self.__next__ ]
def scry(self):
@@ -1496,50 +1306,40 @@ class If(Node):
rv._next = None
return rv
- def restructure(self, callback):
- for _condition, block in self.entries:
- callback(block)
class UserStatement(Node):
- __slots__ = [
- 'line',
- 'parsed',
- 'block',
- 'translatable' ]
-
- def __setstate__(self, state):
- self.block = [ ]
- self.translatable = False
- Node.__setstate__(self, state)
+ __slots__ = [ 'line', 'parsed' ]
- def __init__(self, loc, line, block):
+ def __init__(self, loc, line):
super(UserStatement, self).__init__(loc)
self.line = line
- self.block = block
self.parsed = None
# Do not store the parse quite yet.
- _parse_info = renpy.statements.parse(self, self.line, self.block)
-
+ renpy.statements.parse(self, self.line)
+
def diff_info(self):
return (UserStatement, self.line)
def execute(self):
- next_node(self.get_next())
-
self.call("execute")
+ return self.get_next()
- def predict(self):
- self.call("predict")
- return [ self.get_next() ]
+ def predict(self, callback):
+ predicted = self.call("predict") or [ ]
+ for i in predicted:
+ callback(i)
+
+ return [ self.get_next() ]
+
def call(self, method, *args, **kwargs):
-
- parsed = self.parsed
+
+ parsed = self.parsed
if parsed is None:
- parsed = renpy.statements.parse(self, self.line, self.block)
+ parsed = renpy.statements.parse(self, self.line)
self.parsed = parsed
renpy.statements.call(method, parsed, *args, **kwargs)
@@ -1550,17 +1350,14 @@ class UserStatement(Node):
return renpy.game.script.lookup(rv)
else:
return self.__next__
-
+
def scry(self):
rv = Node.scry(self)
rv._next = self.get_next()
self.call("scry", rv)
return rv
-
- def get_code(self, dialogue_filter=None):
- return self.line
-
-
+
+
class Define(Node):
__slots__ = [
@@ -1577,181 +1374,23 @@ class Define(Node):
"""
super(Define, self).__init__(loc)
-
+
self.varname = name
self.code = PyCode(expr, loc=loc, mode='eval')
-
- def diff_info(self):
+
+ def diff_info(self):
return (Define, tuple(self.varname))
+ def get_pycode(self):
+ if self.code:
+ return [ self.code ]
+ else:
+ return [ ]
+
def execute(self):
- next_node(self.__next__)
-
value = renpy.python.py_eval_bytecode(self.code.bytecode)
- renpy.dump.definitions.append((self.varname, self.filename, self.linenumber))
- setattr(renpy.store, self.varname, value)
-
-
-class Screen(Node):
-
- __slots__ = [
- 'screen',
- ]
-
- def __init__(self, loc, screen):
- """
- @param name: The name of the image being defined.
-
- @param expr: An expression yielding a Displayable that is
- assigned to the image.
- """
-
- super(Screen, self).__init__(loc)
-
- self.screen = screen
-
- def diff_info(self):
- return (Screen, self.screen.name)
-
- def execute(self):
- next_node(self.__next__)
- self.screen.define()
- renpy.dump.screens.append((self.screen.name, self.filename, self.linenumber))
-
-
-################################################################################
-# Translations
-################################################################################
-
-class Translate(Node):
- """
- A translation block, produced either by explicit translation statements
- or implicit translation blocs.
-
- If language is None, when executed this transfers control to the translate
- statement in the current language, if any, and otherwise runs the block.
- If language is not None, causes an error to occur if control reaches this
- statement.
-
- When control normally leaves a translate statement, in any language, it
- goes to the end of the translate statement in the None language.
- """
-
- __slots__ = [
- "identifier",
- "language",
- "block",
- ]
-
- def __init__(self, loc, identifier, language, block):
- super(Translate, self).__init__(loc)
-
- self.identifier = identifier
- self.language = language
- self.block = block
-
- def diff_info(self):
- return (Translate, self.identifier, self.language)
-
- def chain(self, next): #@ReservedAssignment
- self.next = next
- chain_block(self.block, next)
-
- def execute(self):
-
- if self.language is not None:
- next_node(self.__next__)
- raise Exception("Translation nodes cannot be run directly.")
-
- next_node(renpy.game.script.translator.lookup_translate(self.identifier))
- renpy.game.context().translate_identifier = self.identifier
-
- def predict(self):
- node = renpy.game.script.translator.lookup_translate(self.identifier)
- return [ node ]
-
- def scry(self):
- rv = Scry()
- rv._next = renpy.game.script.translator.lookup_translate(self.identifier)
- return rv
-
- def get_children(self):
- return self.block
-
- def restructure(self, callback):
- return callback(self.block)
-
-
-class EndTranslate(Node):
- """
- A node added implicitly after each translate block. It's responsible for
- resetting the translation identifier.
- """
-
- def __init__(self, loc):
- super(EndTranslate, self).__init__(loc)
-
- def diff_info(self):
- return (EndTranslate,)
-
- def execute(self):
- next_node(self.__next__)
- renpy.game.context().translate_identifier = None
-
-
-class TranslateString(Node):
- """
- A node used for translated strings.
- """
-
- __slots__ = [
- "language",
- "old",
- "new"
- ]
-
- def __init__(self, loc, language, old, new):
- super(TranslateString, self).__init__(loc)
- self.language = language
- self.old = old
- self.new = new
-
- def diff_info(self):
- return (TranslateString,)
-
- def execute(self):
- next_node(self.__next__)
- renpy.translation.add_string_translation(self.language, self.old, self.new)
-
-class TranslatePython(Node):
-
- __slots__ = [
- 'language',
- 'code',
- ]
-
- def __init__(self, loc, language, python_code):
- """
- @param code: A PyCode object.
-
- @param hide: If True, the code will be executed with its
- own local dictionary.
- """
-
- super(TranslatePython, self).__init__(loc)
-
- self.language = language
- self.code = PyCode(python_code, loc=loc, mode='exec')
-
- def diff_info(self):
- return (TranslatePython, self.code.source)
-
- def execute(self):
- next_node(self.__next__)
-
- # def early_execute(self):
- # renpy.python.create_store(self.store)
- # renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
-
-
+ renpy.exports.definitions[self.varname].append((self.filename, self.linenumber, "define"))
+ setattr(renpy.store, self.varname, value)
+
+ return self.__next__
diff --git a/ast2json/renpy/atl.py b/ast2json/renpy/atl.py
index a321e6b..01c76d3 100644
--- a/ast2json/renpy/atl.py
+++ b/ast2json/renpy/atl.py
@@ -1,4 +1,4 @@
-# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
+# Copyright 2004-2010 PyTom <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
@@ -23,12 +23,12 @@ import renpy
import random
def compiling(loc):
- file, number = loc #@ReservedAssignment
+ file, number = loc
renpy.game.exception_info = "Compiling ATL code at %s:%d" % (file, number)
def executing(loc):
- file, number = loc #@ReservedAssignment
+ file, number = loc
renpy.game.exception_info = "Executing ATL code at %s:%d" % (file, number)
@@ -69,8 +69,6 @@ PROPERTIES = {
"xalign" : float,
"yalign" : float,
"rotate" : float,
- "rotate_pad" : bool,
- "transform_anchor" : bool,
"xzoom" : float,
"yzoom" : float,
"zoom" : float,
@@ -85,29 +83,20 @@ PROPERTIES = {
"corner2" : (float, float),
"subpixel" : bool,
"delay" : float,
- "xoffset" : float,
- "yoffset" : float,
- "offset" : (int, int),
- "xcenter" : position,
- "ycenter" : position,
}
-
def correct_type(v, b, ty):
"""
Corrects the type of v to match ty. b is used to inform the match.
"""
if ty is position:
- if v is None:
- return None
- else:
- return type(b)(v)
+ return type(b)(v)
else:
return ty(v)
-def interpolate(t, a, b, type): #@ReservedAssignment
+def interpolate(t, a, b, type):
"""
Linearly interpolate the arguments.
"""
@@ -116,10 +105,7 @@ def interpolate(t, a, b, type): #@ReservedAssignment
return b
# Recurse into tuples.
- if isinstance(b, tuple):
- if a is None:
- a = [ None ] * len(b)
-
+ if isinstance(b, tuple):
return tuple(interpolate(t, i, j, ty) for i, j, ty in zip(a, b, type))
# Deal with booleans, nones, etc.
@@ -142,9 +128,6 @@ def interpolate_spline(t, spline):
if isinstance(spline[-1], tuple):
return tuple(interpolate_spline(t, i) for i in zip(*spline))
-
- if spline[0] is None:
- return spline[-1]
if len(spline) == 2:
t_p = 1.0 - t
@@ -180,9 +163,8 @@ class Context(object):
def __init__(self, context):
self.context = context
- def eval(self, expr): #@ReservedAssignment
- expr = renpy.python.escape_unicode(expr)
- return eval(expr, renpy.store.__dict__, self.context) #@UndefinedVariable
+ def eval(self, expr):
+ return eval(expr, renpy.store.__dict__, self.context)
# This is intended to be subclassed by ATLTransform. It takes care of
# managing ATL execution, which allows ATLTransform itself to not care
@@ -194,8 +176,8 @@ class ATLTransformBase(renpy.object.Object):
def __init__(self, atl, context, parameters):
- # The constructor will be called by atltransform.
-
+ super(ATLTransformBase, self).__init__()
+
if parameters is None:
parameters = ATLTransformBase.parameters
@@ -238,11 +220,6 @@ class ATLTransformBase(renpy.object.Object):
requires that t.atl is self.atl.
"""
- super(ATLTransformBase, self).take_execution_state(t)
-
- if t.atl is not self.atl:
- return
-
self.done = t.done
self.block = t.block
self.atl_state = t.atl_state
@@ -250,15 +227,6 @@ class ATLTransformBase(renpy.object.Object):
self.last_transform_event = t.last_transform_event
self.last_child_transform_event = t.last_child_transform_event
- self.st = t.st
- self.at = t.at
- self.st_offset = t.st_offset
- self.at_offset = t.at_offset
-
- if self.child is renpy.display.motion.null:
- self.child = t.child
-
-
def __call__(self, *args, **kwargs):
context = self.context.context.copy()
@@ -269,8 +237,8 @@ class ATLTransformBase(renpy.object.Object):
positional = list(self.parameters.positional)
args = list(args)
-
- child = None
+
+ child = self.child
if not positional and args:
child = args.pop(0)
@@ -301,12 +269,6 @@ class ATLTransformBase(renpy.object.Object):
else:
raise Exception('Parameter %r is not known by ATL Transform.' % k)
- if child is None:
- child = self.child
-
- if child is None:
- child = renpy.display.motion.get_null()
-
# Create a new ATL Transform.
parameters = renpy.ast.ParameterInfo({}, positional, None, None)
@@ -318,11 +280,11 @@ class ATLTransformBase(renpy.object.Object):
parameters=parameters)
rv.take_state(self)
-
+
return rv
- def compile(self): #@ReservedAssignment
+ def compile(self):
"""
Compiles the ATL code into a block. As necessary, updates the
properties.
@@ -359,13 +321,10 @@ class ATLTransformBase(renpy.object.Object):
if self.child.transform_event != self.last_child_transform_event:
self.last_child_transform_event = self.child.transform_event
self.transform_event = self.child.transform_event
-
+
# Hide request.
if trans.hide_request:
self.transform_event = "hide"
-
- if trans.replaced_request:
- self.transform_event = "replaced"
# Notice transform events.
if self.transform_event != self.last_transform_event:
@@ -380,7 +339,7 @@ class ATLTransformBase(renpy.object.Object):
timebase = at
else:
timebase = st
-
+
action, arg, pause = self.block.execute(trans, timebase, self.atl_state, event)
renpy.game.exception_info = old_exception_info
@@ -393,9 +352,11 @@ class ATLTransformBase(renpy.object.Object):
self.done = True
return pause
+
- def predict_one(self):
- self.atl.predict(self.context)
+ def predict(self, callback):
+ self.atl.predict(self.context, callback)
+
def visit(self):
if not self.block:
@@ -413,12 +374,13 @@ class RawStatement(renpy.object.Object):
# Compiles this RawStatement into a Statement, by using ctx to
# evaluate expressions as necessary.
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
raise Exception("Compile not implemented.")
# Predicts the images used by this statement.
- def predict(self, ctx):
+ def predict(self, ctx, callback):
return
+
# The base class for compiled ATL Statements.
class Statement(renpy.object.Object):
@@ -474,16 +436,16 @@ class RawBlock(RawStatement):
self.animation = animation
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
compiling(self.loc)
statements = [ i.compile(ctx) for i in self.statements ]
return Block(self.loc, statements)
- def predict(self, ctx):
+ def predict(self, ctx, callback):
for i in self.statements:
- i.predict(ctx)
+ i.predict(ctx, callback)
# A compiled ATL block.
@@ -544,7 +506,7 @@ class Block(Statement):
return "next", target - start, None
- # Find the statement and try to run it.
+ # Find the statement and try to run it.
stmt = self.statements[index]
action, arg, pause = stmt.execute(trans, target - start, child_state, event)
@@ -573,13 +535,13 @@ class Block(Statement):
loop_end = target - arg
duration = loop_end - loop_start
- if duration <= 0:
- raise Exception("ATL appears to be in an infinite loop.")
-
# Figure how many durations can occur between the
# start of the loop and now.
new_repeats = int((target - loop_start) / duration)
+ if duration <= 0:
+ raise Exception("ATL appears to be in an infinite loop.")
+
if count is not None:
if repeats + new_repeats >= count:
new_repeats = count - repeats
@@ -655,7 +617,7 @@ class RawMultipurpose(RawStatement):
def add_spline(self, name, exprs):
self.splines.append((name, exprs))
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
compiling(self.loc)
@@ -717,7 +679,7 @@ class RawMultipurpose(RawStatement):
splines.append((name, values))
- for expr, _with in self.expressions:
+ for expr, with_ in self.expressions:
try:
value = ctx.eval(expr)
except:
@@ -739,9 +701,9 @@ class RawMultipurpose(RawStatement):
return Interpolation(self.loc, warper, duration, properties, self.revolution, circles, splines)
- def predict(self, ctx):
+ def predict(self, ctx, callback):
- for i, _j in self.expressions:
+ for i, j in self.expressions:
try:
i = ctx.eval(i)
@@ -749,14 +711,17 @@ class RawMultipurpose(RawStatement):
continue
if isinstance(i, ATLTransformBase):
- i.atl.predict(ctx)
+ i.atl.predict(ctx, callback)
return
try:
- renpy.easy.predict(i)
+ i = renpy.easy.displayable(i)
except:
continue
-
+
+ if isinstance(i, renpy.display.core.Displayable):
+ i.predict(callback)
+
# This lets us have an ATL transform as our child.
class RawContainsExpr(RawStatement):
@@ -766,7 +731,7 @@ class RawContainsExpr(RawStatement):
self.expression = expr
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
compiling(self.loc)
child = ctx.eval(self.expression)
return Child(self.loc, child, None)
@@ -781,7 +746,7 @@ class RawChild(RawStatement):
self.children = [ child ]
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
box = renpy.display.layout.MultiBox(layout='fixed')
for i in self.children:
@@ -806,9 +771,9 @@ class Child(Statement):
old_child = trans.raw_child
- if (old_child is not None) and (old_child is not renpy.display.motion.null) and (self.transition is not None):
+ if old_child is not None and self.transition is not None:
child = self.transition(old_widget=old_child,
- new_widget=self.child)
+ new_widget=self.child)
else:
child = self.child
@@ -865,10 +830,10 @@ class Interpolation(Statement):
# Now, the things we change linearly are in the difference
# between the new and old states.
linear = trans.state.diff(newts)
-
+
revolution = None
splines = [ ]
-
+
# Clockwise revolution.
if self.revolution is not None:
@@ -919,19 +884,12 @@ class Interpolation(Statement):
state = (linear, revolution, splines)
- # Ensure that we set things, even if they don't actually
- # change from the old state.
- for k, v in self.properties:
- if k not in linear:
- setattr(trans.state, k, v)
-
else:
linear, revolution, splines = state
-
+
# Linearly interpolate between the things in linear.
for k, (old, new) in linear.items():
value = interpolate(complete, old, new, PROPERTIES[k])
-
setattr(trans.state, k, value)
# Handle the revolution.
@@ -940,7 +898,6 @@ class Interpolation(Statement):
trans.state.angle = interpolate(complete, startangle, endangle, float)
trans.state.radius = interpolate(complete, startradius, endradius, float)
-
# Handle any splines we might have.
for name, values in splines:
value = interpolate_spline(complete, values)
@@ -964,7 +921,7 @@ class RawRepeat(RawStatement):
self.repeats = repeats
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
compiling(self.loc)
@@ -996,12 +953,12 @@ class RawParallel(RawStatement):
super(RawParallel, self).__init__(loc)
self.blocks = [ block ]
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
return Parallel(self.loc, [i.compile(ctx) for i in self.blocks])
- def predict(self, ctx):
+ def predict(self, ctx, callback):
for i in self.blocks:
- i.predict(ctx)
+ i.predict(ctx, callback)
class Parallel(Statement):
@@ -1058,13 +1015,13 @@ class RawChoice(RawStatement):
self.choices = [ (chance, block) ]
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
compiling(self.loc)
return Choice(self.loc, [ (ctx.eval(chance), block.compile(ctx)) for chance, block in self.choices])
- def predict(self, ctx):
- for _i, j in self.choices:
- j.predict(ctx)
+ def predict(self, ctx, callback):
+ for i, j in self.choices:
+ j.predict(ctx, callback)
class Choice(Statement):
@@ -1116,7 +1073,7 @@ class RawTime(RawStatement):
super(RawTime, self).__init__(loc)
self.time = time
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
compiling(self.loc)
return Time(self.loc, ctx.eval(self.time))
@@ -1140,7 +1097,7 @@ class RawOn(RawStatement):
self.handlers = { name : block }
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
compiling(self.loc)
@@ -1151,9 +1108,9 @@ class RawOn(RawStatement):
return On(self.loc, handlers)
- def predict(self, ctx):
+ def predict(self, ctx, callback):
for i in self.handlers.values():
- i.predict(ctx)
+ i.predict(ctx, callback)
class On(Statement):
@@ -1172,6 +1129,7 @@ class On(Statement):
else:
name, start, cstate = state
+
# If we have an external event, and we have a handler for it,
# handle it.
if event in self.handlers:
@@ -1182,7 +1140,6 @@ class On(Statement):
name = event
start = st
cstate = None
-
while True:
@@ -1194,19 +1151,18 @@ class On(Statement):
# If we get a continue, save our state.
if action == "continue":
-
+
# If it comes from a hide block, indicate that.
- if name == "hide" or name == "replaced":
+ if name == "hide":
trans.hide_response = False
- trans.replaced_response = False
-
+
return "continue", (name, start, arg), pause
# If we get a next, then try going to the default
# event, unless we're already in default, in which case we
# go to None.
elif action == "next":
- if name == "default" or name == "hide" or name == "replaced":
+ if name == "default" or name == "hide":
name = None
else:
name = "default"
@@ -1242,7 +1198,7 @@ class RawEvent(RawStatement):
self.name = name
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
return Event(self.loc, self.name)
@@ -1264,11 +1220,10 @@ class RawFunction(RawStatement):
self.expr = expr
- def compile(self, ctx): #@ReservedAssignment
+ def compile(self, ctx):
compiling(self.loc)
return Function(self.loc, ctx.eval(self.expr))
-
class Function(Statement):
def __init__(self, loc, function):
@@ -1283,6 +1238,8 @@ class Function(Statement):
return "continue", None, fr
else:
return "next", 0, None
+
+
# This parses an ATL block.
diff --git a/ast2json/renpy/game.py b/ast2json/renpy/game.py
index d46043f..f297fc9 100644
--- a/ast2json/renpy/game.py
+++ b/ast2json/renpy/game.py
@@ -1,4 +1,4 @@
-# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
+# Copyright 2004-2010 PyTom <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
@@ -22,6 +22,13 @@
# This module is intended to be used as a singleton object.
# It's purpose is to store in one global all of the data that would
# be to annoying to lug around otherwise.
+#
+# Many modules will probablt want to import this using a command like:
+#
+# import renpy.game as game
+#
+# These modules will then be able to access the various globals defined
+# in this module as fields on game.
import renpy
@@ -33,17 +40,25 @@ basepath = None
searchpath = [ ]
# The options that were read off the command line.
-args = None
+options = None
# The game's script.
script = None
+# A shallow copy of the store dictionary made at the end of the init
+# phase. If a key in here points to the same value here as it does in
+# the store, it is not saved.
+clean_store = None
+
# A stack of execution contexts.
contexts = [ ]
# The interface that the game uses to interact with the user.
interface = None
+# Are we still running init blocks?
+init_phase = True
+
# Are we inside lint?
lint = False
@@ -80,9 +95,6 @@ less_updates = False
# Should we never show the mouse?
less_mouse = False
-# Should we not imagedissiolve?
-less_imagedissolve = False
-
# The class that's used to hold the persistent data.
class Persistent(object):
@@ -97,107 +109,75 @@ class Persistent(object):
return None
# The persistent data that's kept from session to session
-persistent = Persistent()
+persistent = None
-class Preferences(renpy.object.Object):
+class Preferences(object):
"""
Stores preferences that will one day be persisted.
"""
- __version__ = 5
-
- def after_upgrade(self, version):
- if version < 1:
- self.mute_volumes = 0
- if version < 2:
- self.using_afm_enable = False
- if version < 3:
- self.physical_size = None
- if version < 4:
- self.renderer = "auto"
- self.performance_test = True
- if version < 5:
- self.language = None
-
- def __init__(self):
- self.fullscreen = False
- self.skip_unseen = False
- self.text_cps = 0
- self.afm_time = 0
- self.afm_enable = True
+ def reinit(self):
+ self.fullscreen = False # W0201
+ self.skip_unseen = False # W0201
+ self.text_cps = 0 # W0201
+ self.afm_time = 0 # W0201
+ self.afm_enable = True # W0201
+
# These will be going away soon.
- self.sound = True
- self.music = True
+ self.sound = True # W0201
+ self.music = True # W0201
+
# 2 - All transitions.
# 1 - Only non-default transitions.
# 0 - No transitions.
- self.transitions = 2
+ self.transitions = 2 # W0201
- self.skip_after_choices = False
+ self.skip_after_choices = False # W0201
# Mixer channel info.
-
- # A map from channel name to the current volume (between 0 and 1).
- self.volumes = { }
-
- # True if the channel should not play music. False
- # otherwise. (Not used anymore.)
- self.mute = { }
+ self.volumes = { } # W0201
+ self.mute = { } # W0201
# Joystick mappings.
- self.joymap = dict(
+ self.joymap = dict( # W0201
joy_left="Axis 0.0 Negative",
joy_right="Axis 0.0 Positive",
joy_up="Axis 0.1 Negative",
joy_down="Axis 0.1 Positive",
joy_dismiss="Button 0.0")
-
- # The size of the window, or None if we don't know it yet.
- self.physical_size = None
-
- # The graphics renderer we use.
- self.renderer = "auto"
-
- # Should we do a performance test on startup?
- self.performance_test = True
- # The language we use for translations.
- self.language = None
-
def set_volume(self, mixer, volume):
+ if volume == 0:
+ self.mute[mixer] = True
+ else:
+ self.mute[mixer] = False
+
self.volumes[mixer] = volume
def get_volume(self, mixer):
- return self.volumes.get(mixer, 0)
+ return self.volumes[mixer]
- def set_mute(self, mixer, mute):
- self.mute[mixer] = mute
+ def __setstate__(self, state):
+ self.reinit()
+ vars(self).update(state)
- def get_mute(self, mixer):
- return self.mute[mixer]
-
-# The current preferences.
-preferences = Preferences()
-
-class RestartContext(Exception):
- """
- Restarts the current context. If `label` is given, calls that label
- in the restarted context.
- """
+ def __init__(self):
+ self.reinit()
- def __init__(self, label):
- self.label = label
+# The current preferences.
+preferences = None
-class RestartTopContext(Exception):
+class RestartException(Exception):
"""
- Restarts the top context. If `label` is given, calls that label
- in the restarted context.
+ This class will be used to convey to the system that the context has
+ been changed, and therefore execution needs to be restarted.
"""
- def __init__(self, label):
+ def __init__(self, contexts, label): # W0231
+ self.contexts = contexts
self.label = label
-
+
class FullRestartException(Exception):
"""
An exception of this type forces a hard restart, completely
@@ -207,6 +187,7 @@ class FullRestartException(Exception):
def __init__(self, reason="end_game"): # W0231
self.reason = reason
+
class UtterRestartException(Exception):
"""
An exception of this type forces an even harder restart, causing
@@ -216,17 +197,9 @@ class UtterRestartException(Exception):
class QuitException(Exception):
"""
An exception of this class will let us force a safe quit, from
- anywhere in the program.
-
- `relaunch`
- If given, the program will run another copy of itself, with the
- same arguments.
+ anywhere in the program. Do not pass go, do not collect $200.
"""
- def __init__(self, relaunch=False):
- Exception.__init__(self)
- self.relaunch = relaunch
-
class JumpException(Exception):
"""
This should be raised with a label as the only argument. This causes
@@ -240,47 +213,11 @@ class JumpOutException(Exception):
the current context, and then raises a JumpException.
"""
-class CallException(Exception):
- """
- Raise this exception to cause the current statement to terminate,
- and control to be transferred to the named label.
- """
-
- def __init__(self, label, args, kwargs):
- Exception.__init__(self)
-
- self.label = label
- self.args = args
- self.kwargs = kwargs
-
-class EndReplay(Exception):
- """
- Raise this exception to end the current replay (the current call to
- call_replay).
- """
-
class ParseErrorException(Exception):
"""
This is raised when a parse error occurs, after it has been
reported to the user.
"""
-
-# A tuple of exceptions that should not be caught by the
-# exception reporting mechanism.
-CONTROL_EXCEPTIONS = (
- RestartContext,
- RestartTopContext,
- FullRestartException,
- UtterRestartException,
- QuitException,
- JumpException,
- JumpOutException,
- CallException,
- EndReplay,
- ParseErrorException,
- KeyboardInterrupt,
- )
-
def context(index=-1):
"""
@@ -290,7 +227,7 @@ def context(index=-1):
return contexts[index]
-def invoke_in_new_context(callable, *args, **kwargs): #@ReservedAssignment
+def invoke_in_new_context(callable, *args, **kwargs):
"""
This pushes the current context, and invokes the given python
function in a new context. When that function returns or raises an
@@ -312,30 +249,18 @@ def invoke_in_new_context(callable, *args, **kwargs): #@ReservedAssignment
inside an interaction.
"""
- context = renpy.execution.Context(False, contexts[-1], clear=True)
+ context = renpy.execution.Context(False, contexts[-1])
contexts.append(context)
- if renpy.display.interface is not None:
- renpy.display.interface.enter_context()
-
try:
-
return callable(*args, **kwargs)
-
- except renpy.game.JumpOutException as e:
-
- raise renpy.game.JumpException(e.args[0])
-
finally:
-
contexts.pop()
- contexts[-1].do_deferred_rollback()
- if interface.restart_interaction and contexts:
+ if interface.restart_interaction:
contexts[-1].scene_lists.focused = None
-
def call_in_new_context(label, *args, **kwargs):
"""
This code creates a new context, and starts executing code from
@@ -347,11 +272,9 @@ def call_in_new_context(label, *args, **kwargs):
inside an interaction.
"""
- context = renpy.execution.Context(False, contexts[-1], clear=True)
+ context = renpy.execution.Context(False, contexts[-1])
contexts.append(context)
- if renpy.display.interface is not None:
- renpy.display.interface.enter_context()
if args:
renpy.store._args = args
@@ -366,72 +289,22 @@ def call_in_new_context(label, *args, **kwargs):
try:
context.goto_label(label)
- renpy.execution.run_context(False)
+ context.run()
- rv = renpy.store._return #@UndefinedVariable
+ rv = renpy.store._return
+ context.pop_all_dynamic()
+ contexts.pop()
return rv
except renpy.game.JumpOutException as e:
+ context.pop_all_dynamic()
+ contexts.pop()
raise renpy.game.JumpException(e.args[0])
finally:
-
- contexts.pop()
- contexts[-1].do_deferred_rollback()
-
- if interface.restart_interaction and contexts:
+ if interface.restart_interaction:
contexts[-1].scene_lists.focused = None
-
-def call_replay(label, scope={}):
- """
- :doc: replay
- Calls a label as a memory.
-
- Keyword arguments are used to set the initial values of variables in the
- memory context.
- """
-
- renpy.game.log.complete()
-
- old_log = renpy.game.log
- renpy.game.log = renpy.python.RollbackLog()
-
- sb = renpy.python.StoreBackup()
- renpy.python.clean_stores()
-
- context = renpy.execution.Context(True)
- contexts.append(context)
-
- if renpy.display.interface is not None:
- renpy.display.interface.enter_context()
-
- for k, v in scope.items():
- setattr(renpy.store, k, v)
- renpy.store._in_replay = label
-
- try:
-
- context.goto_label("_start_replay")
- renpy.execution.run_context(False)
-
- except EndReplay:
- pass
-
- finally:
- contexts.pop()
- renpy.game.log = old_log
- sb.restore()
-
- if interface.restart_interaction and contexts:
- contexts[-1].scene_lists.focused = None
-
-
-# Type information.
-if False:
- script = renpy.script.Script()
- interface = renpy.display.core.Interface()
- log = renpy.python.RollbackLog()
diff --git a/ast2json/renpy/object.py b/ast2json/renpy/object.py
index c595e8c..98b8a16 100644
--- a/ast2json/renpy/object.py
+++ b/ast2json/renpy/object.py
@@ -1,60 +1,60 @@
-# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
-#
-# Permission is hereby granted, free of charge, to any person
-# obtaining a copy of this software and associated documentation files
-# (the "Software"), to deal in the Software without restriction,
-# including without limitation the rights to use, copy, modify, merge,
-# publish, distribute, sublicense, and/or sell copies of the Software,
-# and to permit persons to whom the Software is furnished to do so,
-# subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-class Object(object):
- """
- Our own base class. Contains methods to simplify serialization.
- """
-
- __version__ = 0
-
- nosave = [ ]
-
- def __getstate__(self):
- rv = vars(self).copy()
-
- for f in self.nosave:
- if f in rv:
- del rv[f]
-
-
- rv["__version__"] = self.__version__
-
- return rv
-
-
- # None, to prevent this from being called when unnecessary.
- after_setstate = None
-
- def __setstate__(self, new_dict):
-
- version = new_dict.pop("__version__", 0)
-
- self.__dict__.update(new_dict)
-
- if version != self.__version__:
- self.after_upgrade(version) # E1101
-
- if self.after_setstate:
- self.after_setstate() # E1102
-
-# We don't handle slots with this mechanism, since the call to vars should
-# throw an error.
+# Copyright 2004-2010 PyTom <pytom@bishoujo.us>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class Object(object):
+ """
+ Our own base class. Contains methods to simplify serialization.
+ """
+
+ __version__ = 0
+
+ nosave = [ ]
+
+ def __getstate__(self):
+ rv = vars(self).copy()
+
+ for f in self.nosave:
+ if f in rv:
+ del rv[f]
+
+
+ rv["__version__"] = self.__version__
+
+ return rv
+
+
+ # None, to prevent this from being called when unnecessary.
+ after_setstate = None
+
+ def __setstate__(self, new_dict):
+
+ version = new_dict.pop("__version__", 0)
+
+ self.__dict__.update(new_dict)
+
+ if version != self.__version__:
+ self.after_upgrade(version) # E1101
+
+ if self.after_setstate:
+ self.after_setstate() # E1102
+
+# We don't handle slots with this mechanism, since the call to vars should
+# throw an error.
diff --git a/ast2json/renpy/parser.py b/ast2json/renpy/parser.py
new file mode 100644
index 0000000..c0d9164
--- /dev/null
+++ b/ast2json/renpy/parser.py
@@ -0,0 +1,1842 @@
+# Copyright 2004-2010 PyTom <pytom@bishoujo.us>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# This module contains the parser for the Ren'Py script language. It's
+# called when parsing is necessary, and creats an AST from the script.
+
+import codecs
+import re
+import os
+import os.path
+
+import renpy
+import renpy.ast as ast
+
+# A list of parse error messages.
+parse_errors = [ ]
+
+class ParseError(Exception):
+
+ def __init__(self, filename, number, msg, line=None, pos=None, first=False):
+ message = "On line %d of %s: %s" % (number, unicode_filename(filename), msg)
+
+ if line:
+ lines = line.split('\n')
+
+ if len(lines) > 1:
+ open_string = None
+ i = 0
+
+ while i < len(lines[0]):
+ c = lines[0][i]
+
+ if c == "\\":
+ i += 1
+ elif c == open_string:
+ open_string = None
+ elif open_string:
+ pass
+ elif c == '`' or c == '\'' or c == '"':
+ open_string = c
+
+ i += 1
+
+ if open_string:
+ message += "\n(Perhaps you left out a %s at the end of the first line.)" % open_string
+
+ for l in lines:
+ message += "\n" + l
+
+ if pos is not None:
+ if pos <= len(l):
+ message += "\n" + " " * pos + "^"
+ pos = None
+ else:
+ pos -= len(l)
+
+ if first:
+ break
+
+ self.message = message
+
+ Exception.__init__(self, message)
+
+ def __unicode__(self):
+ return self.message
+
+# Something to hold the expected line number.
+class LineNumberHolder(object):
+ """
+ Holds the expected line number.
+ """
+
+ def __init__(self):
+ self.line = 0
+
+def unicode_filename(fn):
+ """
+ Converts the supplied filename to unicode.
+ """
+
+ if isinstance(fn, str):
+ return fn
+
+ # Windows.
+ try:
+ return fn.decode("mbcs")
+ except:
+ pass
+
+ # Mac and (sane) Unix
+ try:
+ return fn.decode("utf-8")
+ except:
+ pass
+
+ # Insane systems, mojibake.
+ return fn.decode("latin-1")
+
+# Matches either a word, or something else. Most magic is taken care of
+# before this.
+lllword = re.compile(r'__(\w+)|\w+| +|.', re.S)
+
+def munge_filename(fn):
+ # The prefix that's used when __ is found in the file.
+ rv = os.path.basename(fn)
+ rv = os.path.splitext(rv)[0]
+ rv = rv.replace(" ", "_")
+
+ def munge_char(m):
+ return hex(ord(m.group(0)))
+
+ rv = re.sub(r'[^a-zA-Z0-9_]', munge_char, rv)
+
+ return "_m1_" + rv + "__"
+
+def list_logical_lines(filename):
+ """
+ This reads the specified filename, and divides it into logical
+ line. The return value of this function is a list of (filename,
+ line number, line text) triples.
+ """
+
+ f = codecs.open(filename, "r", "utf-8")
+ data = f.read()
+ f.close()
+
+ data = data.replace("\r\n", "\n")
+ data = data.replace("\r", "\n")
+
+ if "RENPY_PATH_ELIDE" in os.environ:
+ old, new = os.environ["RENPY_PATH_ELIDE"].split(':')
+ filename = filename.replace(old, new)
+
+ prefix = munge_filename(filename)
+
+ # Add some newlines, to fix lousy editors.
+ data += "\n\n"
+
+ # The result.
+ rv = []
+
+ # The line number in the physical file.
+ number = 1
+
+ # The current position we're looking at in the buffer.
+ pos = 0
+
+ # Skip the BOM, if any.
+ if len(data) and data[0] == '\ufeff':
+ pos += 1
+
+ # Looping over the lines in the file.
+ while pos < len(data):
+
+ # The line number of the start of this logical line.
+ start_number = number
+
+ # The line that we're building up.
+ line = ""
+
+ # The number of open parenthesis there are right now.
+ parendepth = 0
+
+ # Looping over the characters in a single logical line.
+ while pos < len(data):
+
+ c = data[pos]
+
+ if c == '\t':
+ raise Exception("%s contains a tab character on line %d. Tab characters are not allowed in Ren'Py scripts." % (filename, number))
+
+ if c == '\n':
+ number += 1
+
+ if c == '\n' and not parendepth:
+ # If not blank...
+ if not re.match("^\s*$", line):
+
+ # Add to the results.
+ rv.append((filename, start_number, line))
+
+ pos += 1
+ # This helps out error checking.
+ line = ""
+ break
+
+ # Backslash/newline.
+ if c == "\\" and data[pos+1] == "\n":
+ pos += 2
+ number += 1
+ line += "\\\n"
+ continue
+
+ # Parenthesis.
+ if c in ('(', '[', '{'):
+ parendepth += 1
+
+ if c in ('}', ']', ')') and parendepth:
+ parendepth -= 1
+
+ # Comments.
+ if c == '#':
+ while data[pos] != '\n':
+ pos += 1
+
+ continue
+
+ # Strings.
+ if c in ('"', "'", "`"):
+ delim = c
+ line += c
+ pos += 1
+
+ escape = False
+
+ while pos < len(data):
+
+ c = data[pos]
+
+ if c == '\n':
+ number += 1
+
+ if escape:
+ escape = False
+ pos += 1
+ line += c
+ continue
+
+ if c == delim:
+ pos += 1
+ line += c
+ break
+
+ if c == '\\':
+ escape = True
+
+ line += c
+ pos += 1
+
+ continue
+
+ continue
+
+ m = lllword.match(data, pos)
+
+ word = m.group(0)
+ rest = m.group(1)
+
+ if rest and "__" not in rest:
+ word = prefix + rest
+
+ line += word
+ pos = m.end(0)
+
+ # print repr(data[pos:])
+
+
+ if not line == "":
+ raise ParseError(filename, start_number, "is not terminated with a newline. (Check strings and parenthesis.)", line=line, first=True)
+
+ return rv
+
+
+
+def group_logical_lines(lines):
+ """
+ This takes as input the list of logical line triples output from
+ list_logical_lines, and breaks the lines into blocks. Each block
+ is represented as a list of (filename, line number, line text,
+ block) triples, where block is a block list (which may be empty if
+ no block is associated with this line.)
+ """
+
+ # Returns the depth of a line, and the rest of the line.
+ def depth_split(l):
+
+ depth = 0
+ index = 0
+
+ while True:
+ if l[index] == ' ':
+ depth += 1
+ index += 1
+ continue
+
+ # if l[index] == '\t':
+ # index += 1
+ # depth = depth + 8 - (depth % 8)
+ # continue
+
+ break
+
+ return depth, l[index:]
+
+ # i, min_depth -> block, new_i
+ def gll_core(i, min_depth):
+
+ rv = []
+ depth = None
+
+ while i < len(lines):
+
+ filename, number, text = lines[i]
+
+ line_depth, rest = depth_split(text)
+
+ # This catches a block exit.
+ if line_depth < min_depth:
+ break
+
+ if depth is None:
+ depth = line_depth
+
+ if depth != line_depth:
+ raise ParseError(filename, number, "indentation mismatch.")
+
+ # Advance to the next line.
+ i += 1
+
+ # Try parsing a block associated with this line.
+ block, i = gll_core(i, depth + 1)
+
+ rv.append((filename, number, rest, block))
+
+ return rv, i
+
+ return gll_core(0, 0)[0]
+
+class Lexer(object):
+ """
+ The lexer that is used to lex script files. This works on the idea
+ that we want to lex each line in a block individually, and use
+ sub-lexers to lex sub-blocks.
+ """
+
+ # A list of keywords which should not be parsed as names, because
+ # there is a huge chance of confusion.
+ keywords = set([
+ 'as',
+ 'at',
+ 'behind',
+ 'call',
+ 'expression',
+ 'hide',
+ 'if',
+ 'image',
+ 'init',
+ 'jump',
+ 'menu',
+ 'onlayer',
+ 'python',
+ 'return',
+ 'scene',
+ 'set',
+ 'show',
+ 'with',
+ 'while',
+ 'zorder',
+ 'transform',
+ ])
+
+
+ def __init__(self, block, init=False):
+
+ # Are we underneath an init block?
+ self.init = init
+
+ self.block = block
+ self.eob = False
+
+ self.line = -1
+
+ # These are set by advance.
+ self.filename = ""
+ self.text = ""
+ self.number = 0
+ self.subblock = [ ]
+ self.pos = 0
+ self.word_cache_pos = -1
+ self.word_cache_newpos = -1
+ self.word_cache = ""
+
+ def advance(self):
+ """
+ Advances this lexer to the next line in the block. The lexer
+ starts off before the first line, so advance must be called
+ before any matching can be done. Returns True if we've
+ successfully advanced to a line in the block, or False if we
+ have advanced beyond all lines in the block. In general, once
+ this method has returned False, the lexer is in an undefined
+ state, and it doesn't make sense to call any method other than
+ advance (which will always return False) on the lexer.
+ """
+
+ self.line += 1
+
+ if self.line >= len(self.block):
+ self.eob = True
+ return
+
+ self.filename, self.number, self.text, self.subblock = self.block[self.line]
+ self.pos = 0
+ self.word_cache_pos = -1
+
+ return True
+
+ def match_regexp(self, regexp):
+ """
+ Tries to match the given regexp at the current location on the
+ current line. If it succeds, it returns the matched text (if
+ any), and updates the current position to be after the
+ match. Otherwise, returns None and the position is unchanged.
+ """
+
+ if self.eob:
+ return None
+
+ if self.pos == len(self.text):
+ return None
+
+ m = re.compile(regexp, re.DOTALL).match(self.text, self.pos)
+
+ if not m:
+ return None
+
+ self.pos = m.end()
+
+ return m.group(0)
+
+ def skip_whitespace(self):
+ """
+ Advances the current position beyond any contiguous whitespace.
+ """
+
+ # print self.text[self.pos].encode('unicode_escape')
+
+ self.match_regexp(r"(\s+|\\\n)+")
+
+ def match(self, regexp):
+ """
+ Matches something at the current position, skipping past
+ whitespace. Even if we can't match, the current position is
+ still skipped past the leading whitespace.
+ """
+
+ self.skip_whitespace()
+ return self.match_regexp(regexp)
+
+
+ def keyword(self, word):
+ """
+ Matches a keyword at the current position. A keyword is a word
+ that is surrounded by things that aren't words, like
+ whitespace. (This prevents a keyword from matching a prefix.)
+ """
+
+ oldpos = self.pos
+ if self.word() == word:
+ return word
+
+ self.pos = oldpos
+ return ''
+
+
+ def error(self, msg):
+ """
+ Convenience function for reporting a parse error at the current
+ location.
+ """
+
+ raise ParseError(self.filename, self.number, msg, self.text, self.pos)
+
+ def eol(self):
+ """
+ Returns True if, after skipping whitespace, the current
+ position is at the end of the end of the current line, or
+ False otherwise.
+ """
+
+ self.skip_whitespace()
+ return self.pos >= len(self.text)
+
+ def expect_eol(self):
+ """
+ If we are not at the end of the line, raise an error.
+ """
+
+ if not self.eol():
+ self.error('end of line expected.')
+
+ def expect_noblock(self, stmt):
+ """
+ Called to indicate this statement does not expect a block.
+ If a block is found, raises an error.
+ """
+
+ if self.subblock:
+ self.error('%s does not expect a block. Please check the indentation of the line after this one.' % stmt)
+
+ def expect_block(self, stmt):
+ """
+ Called to indicate that the statement requires that a non-empty
+ block is present.
+ """
+
+ if not self.subblock:
+ self.error('%s expects a non-empty block.' % stmt)
+
+
+ def subblock_lexer(self, init=False):
+ """
+ Returns a new lexer object, equiped to parse the block
+ associated with this line.
+ """
+
+ init = self.init or init
+
+ return Lexer(self.subblock, init=init)
+
+ def string(self):
+ """
+ Lexes a string, and returns the string to the user, or none if
+ no string could be found. This also takes care of expanding
+ escapes and collapsing whitespace.
+
+ Be a little careful, as this can return an empty string, which is
+ different than None.
+ """
+
+ s = self.match(r'r?"([^\\"]|\\.)*"')
+
+ if s is None:
+ s = self.match(r"r?'([^\\']|\\.)*'")
+
+ if s is None:
+ s = self.match(r"r?`([^\\`]|\\.)*`")
+
+ if s is None:
+ return None
+
+ if s[0] == 'r':
+ raw = True
+ s = s[1:]
+ else:
+ raw = False
+
+ # Strip off delimiters.
+ s = s[1:-1]
+
+ if not raw:
+
+ # Collapse runs of whitespace into single spaces.
+ s = re.sub(r'\s+', ' ', s)
+
+ s = s.replace("\\n", "\n")
+ s = s.replace("\\{", "{{")
+ s = s.replace("\\%", "%%")
+ s = re.sub(r'\\u([0-9a-fA-F]{1,4})',
+ lambda m : chr(int(m.group(1), 16)), s)
+ s = re.sub(r'\\(.)', r'\1', s)
+
+ return s
+
+ def integer(self):
+ """
+ Tries to parse an integer. Returns a string containing the
+ integer, or None.
+ """
+
+ return self.match(r'(\+|\-)?\d+')
+
+ def float(self):
+ """
+ Tries to parse a number (float). Returns a string containing the
+ number, or None.
+ """
+
+ return self.match(r'(\+|\-)?(\d+\.?\d*|\.\d+)([eE][-+]?\d+)?')
+
+ def word(self):
+ """
+ Parses a name, which may be a keyword or not.
+ """
+
+ if self.pos == self.word_cache_pos:
+ self.pos = self.word_cache_newpos
+ return self.word_cache
+
+ self.word_cache_pos = self.pos
+ rv = self.match(r'[a-zA-Z_\u00a0-\ufffd][0-9a-zA-Z_\u00a0-\ufffd]*')
+ self.word_cache = rv
+ self.word_cache_newpos = self.pos
+
+ return rv
+
+
+ def name(self):
+ """
+ This tries to parse a name. Returns the name or None.
+ """
+
+ oldpos = self.pos
+ rv = self.word()
+
+ if rv in self.keywords:
+ self.pos = oldpos
+ return None
+
+ return rv
+
+ def python_string(self):
+ """
+ This tries to match a python string at the current
+ location. If it matches, it returns True, and the current
+ position is updated to the end of the string. Otherwise,
+ returns False.
+ """
+
+ if self.eol():
+ return False
+
+ c = self.text[self.pos]
+
+ # Allow unicode strings.
+ if c == 'u':
+ self.pos += 1
+
+ if self.pos == len(self.text):
+ self.pos -= 1
+ return False
+
+ c = self.text[self.pos]
+
+ if c not in ('"', "'"):
+ self.pos -= 1
+ return False
+
+ elif c not in ('"', "'"):
+ return False
+
+ delim = c
+
+ while True:
+ self.pos += 1
+
+ if self.eol():
+ self.error("end of line reached while parsing string.")
+
+ c = self.text[self.pos]
+
+ if c == delim:
+ break
+
+ if c == '\\':
+ self.pos += 1
+
+ self.pos += 1
+ return True
+
+
+ def dotted_name(self):
+ """
+ This tries to match a dotted name, which is one or more names,
+ separated by dots. Returns the dotted name if it can, or None
+ if it cannot.
+
+ Once this sees the first name, it commits to parsing a
+ dotted_name. It will report an error if it then sees a dot
+ without a name behind it.
+ """
+
+ rv = self.name()
+
+ if not rv:
+ return None
+
+ while self.match(r'\.'):
+ n = self.name()
+ if not n:
+ self.error('expecting name.')
+
+ rv += "." + n
+
+ return rv
+
+ def delimited_python(self, delim):
+ """
+ This matches python code up to, but not including, the non-whitespace
+ delimiter characters. Returns a string containing the matched code,
+ which may be empty if the first thing is the delimiter. Raises an
+ error if EOL is reached before the delimiter.
+ """
+
+ start = self.pos
+
+ while not self.eol():
+
+ c = self.text[self.pos]
+
+ if c in delim:
+ return renpy.ast.PyExpr(self.text[start:self.pos], self.filename, self.number)
+
+ if c == '"' or c == "'":
+ self.python_string()
+ continue
+
+ if self.parenthesised_python():
+ continue
+
+ self.pos += 1
+
+ self.error("reached end of line when expecting '%s'." % delim)
+
+ def python_expression(self):
+ """
+ Returns a python expression, which is arbitrary python code
+ extending to a colon.
+ """
+
+ pe = self.delimited_python(':')
+
+ if not pe:
+ self.error("expected python_expression")
+
+ rv = renpy.ast.PyExpr(pe.strip(), pe.filename, pe.linenumber) # E1101
+
+ return rv
+
+ def parenthesised_python(self):
+ """
+ Tries to match a parenthesised python expression. If it can,
+ returns true and updates the current position to be after the
+ closing parenthesis. Returns False otherewise.
+ """
+
+ c = self.text[self.pos]
+
+ if c == '(':
+ self.pos += 1
+ self.delimited_python(')')
+ self.pos += 1
+ return True
+
+ if c == '[':
+ self.pos += 1
+ self.delimited_python(']')
+ self.pos += 1
+ return True
+
+
+ if c == '{':
+ self.pos += 1
+ self.delimited_python('}')
+ self.pos += 1
+ return True
+
+ return False
+
+
+ def simple_expression(self):
+ """
+ Tries to parse a simple_expression. Returns the text if it can, or
+ None if it cannot.
+ """
+
+ self.skip_whitespace()
+ if self.eol():
+ return None
+
+ start = self.pos
+
+ # We start with either a name, a python_string, or parenthesized
+ # python
+ if (not self.python_string() and
+ not self.name() and
+ not self.float() and
+ not self.parenthesised_python()):
+
+ return None
+
+ while not self.eol():
+ self.skip_whitespace()
+
+ if self.eol():
+ break
+
+ # If we see a dot, expect a dotted name.
+ if self.match(r'\.'):
+ n = self.name()
+ if not n:
+ self.error("expecting name after dot.")
+
+ continue
+
+ # Otherwise, try matching parenthesised python.
+ if self.parenthesised_python():
+ continue
+
+ break
+
+ return self.text[start:self.pos]
+
+ def checkpoint(self):
+ """
+ Returns an opaque representation of the lexer state. This can be
+ passed to revert to back the lexer up.
+ """
+
+ return self.filename, self.number, self.text, self.subblock, self.pos
+
+ def revert(self, state):
+ """
+ Reverts the lexer to the given state. State must have been returned
+ by a previous checkpoint operation on this lexer.
+ """
+
+ self.filename, self.number, self.text, self.subblock, self.pos = state
+ self.word_cache_pos = -1
+
+ def get_location(self):
+ """
+ Returns a (filename, line number) tuple representing the current
+ physical location of the start of the current logical line.
+ """
+
+ return self.filename, self.number
+
+ def require(self, thing, name=None):
+ """
+ Tries to parse thing, and reports an error if it cannot be done.
+
+ If thing is a string, tries to parse it using
+ self.match(thing). Otherwise, thing must be a method on this lexer
+ object, which is called directly.
+ """
+
+ if isinstance(thing, str):
+ name = name or thing
+ rv = self.match(thing)
+ else:
+ name = name or thing.__func__.__name__
+ rv = thing()
+
+ if rv is None:
+ self.error("expected '%s' not found." % name)
+
+ return rv
+
+ def rest(self):
+ """
+ Skips whitespace, then returns the rest of the current
+ line, and advances the current position to the end of
+ the current line.
+ """
+
+ self.skip_whitespace()
+
+ pos = self.pos
+ self.pos = len(self.text)
+ return self.text[pos:]
+
+ def python_block(self):
+ """
+ Returns the subblock of this code, and subblocks of that
+ subblock, as indented python code. This tries to insert
+ whitespace to ensure line numbers match up.
+ """
+
+ rv = [ ]
+
+ o = LineNumberHolder()
+ o.line = self.number
+
+ def process(block, indent):
+
+ for fn, ln, text, subblock in block:
+
+ if o.line > ln:
+ assert False
+
+ while o.line < ln:
+ rv.append(indent + '\n')
+ o.line += 1
+
+ linetext = indent + text + '\n'
+
+ rv.append(linetext)
+ o.line += linetext.count('\n')
+
+ process(subblock, indent + ' ')
+
+ process(self.subblock, '')
+ return ''.join(rv)
+
+def parse_image_name(l):
+ """
+ This parses an image name, and returns it as a tuple. It requires
+ that the image name be present.
+ """
+
+ rv = [ l.require(l.name) ]
+
+ while True:
+ n = l.simple_expression()
+ if not n:
+ break
+
+ rv.append(n.strip())
+
+ return tuple(rv)
+
+def parse_simple_expression_list(l):
+ """
+ This parses a comma-separated list of simple_expressions, and
+ returns a list of strings. It requires at least one
+ simple_expression be present.
+ """
+
+ rv = [ l.require(l.simple_expression) ]
+
+ while True:
+ if not l.match(','):
+ break
+
+ e = l.simple_expression()
+
+ if not e:
+ break
+
+ rv.append(e)
+
+ return rv
+
+def parse_image_specifier(l):
+ """
+ This parses an image specifier.
+ """
+
+ tag = None
+ layer = None
+ at_list = [ ]
+ zorder = None
+ behind = [ ]
+
+ if l.keyword("expression") or l.keyword("image"):
+ expression = l.require(l.simple_expression)
+ image_name = ( expression.strip(), )
+ else:
+ image_name = parse_image_name(l)
+ expression = None
+
+ while True:
+
+ if l.keyword("onlayer"):
+ if layer:
+ l.error("multiple onlayer clauses are prohibited.")
+ else:
+ layer = l.require(l.name)
+
+ continue
+
+ if l.keyword("at"):
+
+ if at_list:
+ l.error("multiple at clauses are prohibited.")
+ else:
+ at_list = parse_simple_expression_list(l)
+
+ continue
+
+ if l.keyword("as"):
+
+ if tag:
+ l.error("multiple as clauses are prohibited.")
+ else:
+ tag = l.require(l.name)
+
+ continue
+
+ if l.keyword("zorder"):
+
+ if zorder is not None:
+ l.error("multiple zorder clauses are prohibited.")
+ else:
+ zorder = l.require(l.simple_expression)
+
+ continue
+
+ if l.keyword("behind"):
+
+ if behind:
+ l.error("multiple behind clauses are prohibited.")
+
+ while True:
+ bhtag = l.require(l.name)
+ behind.append(bhtag)
+ if not l.match(','):
+ break
+
+ continue
+
+ break
+
+ if layer is None:
+ layer = 'master'
+
+
+
+ return image_name, expression, tag, at_list, layer, zorder, behind
+
+def parse_with(l, node):
+ """
+ Tries to parse the with clause associated with this statement. If
+ one exists, then the node is wrapped in a list with the
+ appropriate pair of With nodes. Otherwise, just returns the
+ statement by itself.
+ """
+
+ loc = l.get_location()
+
+ if not l.keyword('with'):
+ return node
+
+ expr = l.require(l.simple_expression)
+
+ return [ ast.With(loc, "None", expr),
+ node,
+ ast.With(loc, expr) ]
+
+
+
+def parse_menu(stmtl, loc):
+
+ l = stmtl.subblock_lexer()
+
+ has_choice = False
+
+ has_say = False
+ has_caption = False
+
+ with_ = None
+ set = None
+
+ say_who = None
+ say_what = None
+
+ # Tuples of (label, condition, block)
+ items = [ ]
+
+ l.advance()
+
+ while not l.eob:
+
+ if l.keyword('with'):
+ with_ = l.require(l.simple_expression)
+ l.expect_eol()
+ l.expect_noblock('with clause')
+ l.advance()
+
+ continue
+
+ if l.keyword('set'):
+ set = l.require(l.simple_expression)
+ l.expect_eol()
+ l.expect_noblock('set menuitem')
+ l.advance()
+
+ continue
+
+ # Try to parse a say menuitem.
+ state = l.checkpoint()
+
+ who = l.simple_expression()
+ what = l.string()
+
+ if who is not None and what is not None:
+
+ l.expect_eol()
+ l.expect_noblock("say menuitem")
+
+ if has_caption:
+ l.error("Say menuitems and captions may not exist in the same menu.")
+
+ if has_say:
+ l.error("Only one say menuitem may exist per menu.")
+
+ has_say = True
+ say_who = who
+ say_what = what
+
+ l.advance()
+
+ continue
+
+ l.revert(state)
+
+
+ label = l.string()
+
+ if label is None:
+ l.error('expected menuitem')
+
+ # A string on a line by itself is a caption.
+ if l.eol():
+ l.expect_noblock('caption menuitem')
+
+ if label and has_say:
+ l.error("Captions and say menuitems may not exist in the same menu.")
+
+ # Only set this if the caption is not "".
+ if label:
+ has_caption = True
+
+ items.append((label, "True", None))
+ l.advance()
+
+ continue
+
+ # Otherwise, we have a choice.
+ has_choice = True
+
+ condition = "True"
+
+ if l.keyword('if'):
+ condition = l.require(l.python_expression)
+
+ l.require(':')
+ l.expect_eol()
+ l.expect_block('choice menuitem')
+
+ block = parse_block(l.subblock_lexer())
+
+ items.append((label, condition, block))
+ l.advance()
+
+ if not has_choice:
+ stmtl.error("Menu does not contain any choices.")
+
+ rv = [ ]
+ if has_say:
+ rv.append(ast.Say(loc, say_who, say_what, None, interact=False))
+
+ rv.append(ast.Menu(loc, items, set, with_))
+
+ return rv
+
+def parse_parameters(l):
+
+ parameters = [ ]
+ positional = [ ]
+ extrapos = None
+ extrakw = None
+
+ add_positional = True
+
+ names = set()
+
+ if not l.match(r'\('):
+ return None
+
+ while True:
+
+ if l.match('\)'):
+ break
+
+ if l.match(r'\*\*'):
+
+ if extrakw is not None:
+ l.error('a label may have only one ** parameter')
+
+ extrakw = l.require(l.name)
+
+ if extrakw in names:
+ l.error('parameter %s appears twice.' % extrakw)
+
+ names.add(extrakw)
+
+
+ elif l.match(r'\*'):
+
+ if not add_positional:
+ l.error('a label may have only one * parameter')
+
+ add_positional = False
+
+ extrapos = l.name()
+
+ if extrapos is not None:
+
+ if extrapos in names:
+ l.error('parameter %s appears twice.' % extrapos)
+
+ names.add(extrapos)
+
+ else:
+
+ name = l.require(l.name)
+
+ if name in names:
+ l.error('parameter %s appears twice.' % name)
+
+ names.add(name)
+
+ if l.match(r'='):
+ default = l.delimited_python("),")
+ else:
+ default = None
+
+ parameters.append((name, default))
+
+ if add_positional:
+ positional.append(name)
+
+ if l.match(r'\)'):
+ break
+
+ l.require(r',')
+
+ return renpy.ast.ParameterInfo(parameters, positional, extrapos, extrakw)
+
+def parse_arguments(l):
+ """
+ Parse a list of arguments, if one is present.
+ """
+
+ arguments = [ ]
+ extrakw = None
+ extrapos = None
+
+ if not l.match(r'\('):
+ return None
+
+ while True:
+
+ if l.match('\)'):
+ break
+
+ if l.match(r'\*\*'):
+
+ if extrakw is not None:
+ l.error('a call may have only one ** argument')
+
+ extrakw = l.delimited_python("),")
+
+
+ elif l.match(r'\*'):
+ if extrapos is not None:
+ l.error('a call may have only one * argument')
+
+ extrapos = l.delimited_python("),")
+
+ else:
+
+ state = l.checkpoint()
+
+ name = l.name()
+ if not (name and l.match(r'=')):
+ l.revert(state)
+ name = None
+
+ arguments.append((name, l.delimited_python("),")))
+
+ if l.match(r'\)'):
+ break
+
+ l.require(r',')
+
+ return renpy.ast.ArgumentInfo(arguments, extrapos, extrakw)
+
+
+
+
+def parse_statement(l):
+ """
+ This parses a Ren'Py statement. l is expected to be a Ren'Py lexer
+ that has been advanced to a logical line. This function will
+ advance l beyond the last logical line making up the current
+ statement, and will return an AST object representing this
+ statement, or a list of AST objects representing this statement.
+ """
+
+ # Store the current location.
+ loc = l.get_location()
+
+ ### If statement
+ if l.keyword('if'):
+ entries = [ ]
+
+ condition = l.require(l.python_expression)
+ l.require(':')
+ l.expect_eol()
+ l.expect_block('if statement')
+
+ block = parse_block(l.subblock_lexer())
+
+ entries.append((condition, block))
+
+ l.advance()
+
+ while l.keyword('elif'):
+
+ condition = l.require(l.python_expression)
+ l.require(':')
+ l.expect_eol()
+ l.expect_block('elif clause')
+
+ block = parse_block(l.subblock_lexer())
+
+ entries.append((condition, block))
+
+ l.advance()
+
+ if l.keyword('else'):
+ l.require(':')
+ l.expect_eol()
+ l.expect_block('else clause')
+
+ block = parse_block(l.subblock_lexer())
+
+ entries.append(('True', block))
+
+ l.advance()
+
+ return ast.If(loc, entries)
+
+ if l.keyword('elif'):
+ l.error('elif clause must be associated with an if statement.')
+
+ if l.keyword('else'):
+ l.error('else clause must be associated with an if statement.')
+
+
+ ### While statement
+ if l.keyword('while'):
+ condition = l.require(l.python_expression)
+ l.require(':')
+ l.expect_eol()
+ l.expect_block('while statement')
+ block = parse_block(l.subblock_lexer())
+ l.advance()
+
+ return ast.While(loc, condition, block)
+
+
+ ### Pass statement
+ if l.keyword('pass'):
+ l.expect_noblock('pass statement')
+ l.expect_eol()
+ l.advance()
+
+ return ast.Pass(loc)
+
+
+ ### Menu statement.
+ if l.keyword('menu'):
+ l.expect_block('menu statement')
+ label = l.name()
+ l.require(':')
+ l.expect_eol()
+
+ menu = parse_menu(l, loc)
+
+ l.advance()
+
+ rv = [ ]
+
+ if label:
+ rv.append(ast.Label(loc, label, [], None))
+
+ rv.extend(menu)
+
+ return rv
+
+ ### Return statement.
+ if l.keyword('return'):
+ l.expect_noblock('return statement')
+
+ rest = l.rest()
+ if not rest:
+ rest = None
+
+ l.expect_eol()
+ l.advance()
+
+ return ast.Return(loc, rest)
+
+ ### Jump statement
+ if l.keyword('jump'):
+ l.expect_noblock('jump statement')
+
+ if l.keyword('expression'):
+ expression = True
+ target = l.require(l.simple_expression)
+ else:
+ expression = False
+ target = l.require(l.name)
+
+ l.expect_eol()
+ l.advance()
+
+ return ast.Jump(loc, target, expression)
+
+
+ ### Call/From statement.
+ if l.keyword('call'):
+ l.expect_noblock('call statment')
+
+ if l.keyword('expression'):
+ expression = True
+ target = l.require(l.simple_expression)
+
+ else:
+ expression = False
+ target = l.require(l.name)
+
+ # Optional pass, to let someone write:
+ # call expression foo pass (bar, baz)
+ l.keyword('pass')
+
+ arguments = parse_arguments(l)
+
+ rv = [ ast.Call(loc, target, expression, arguments) ]
+
+ if l.keyword('from'):
+ name = l.require(l.name)
+ rv.append(ast.Label(loc, name, [], None))
+ else:
+ rv.append(ast.Pass(loc))
+
+ l.expect_eol()
+ l.advance()
+
+ return rv
+
+ ### Scene statement.
+ if l.keyword('scene'):
+
+ if l.keyword('onlayer'):
+ layer = l.require(l.name)
+ else:
+ layer = "master"
+
+ # Empty.
+ if l.eol():
+ l.advance()
+ return ast.Scene(loc, None, layer)
+
+ imspec = parse_image_specifier(l)
+ stmt = ast.Scene(loc, imspec, imspec[4])
+ rv = parse_with(l, stmt)
+
+ if l.match(':'):
+ stmt.atl = renpy.atl.parse_atl(l.subblock_lexer())
+ else:
+ l.expect_noblock('scene statement')
+
+ l.expect_eol()
+ l.advance()
+
+ return rv
+
+ ### Show statement.
+ if l.keyword('show'):
+ imspec = parse_image_specifier(l)
+ stmt = ast.Show(loc, imspec)
+ rv = parse_with(l, stmt)
+
+ if l.match(':'):
+ stmt.atl = renpy.atl.parse_atl(l.subblock_lexer())
+ else:
+ l.expect_noblock('show statement')
+
+ l.expect_eol()
+ l.advance()
+
+ return rv
+
+ ### Hide statement.
+ if l.keyword('hide'):
+ imspec = parse_image_specifier(l)
+ rv = parse_with(l, ast.Hide(loc, imspec))
+
+ l.expect_eol()
+ l.expect_noblock('hide statement')
+ l.advance()
+
+ return rv
+
+ ### With statement.
+ if l.keyword('with'):
+ expr = l.require(l.simple_expression)
+ l.expect_eol()
+ l.expect_noblock('with statement')
+ l.advance()
+
+ return ast.With(loc, expr)
+
+ ### Image statement.
+ if l.keyword('image'):
+
+ name = parse_image_name(l)
+
+ if l.match(':'):
+ l.expect_eol()
+ expr = None
+ atl = renpy.atl.parse_atl(l.subblock_lexer())
+ else:
+ l.require('=')
+ expr = l.rest()
+ atl = None
+ l.expect_noblock('image statement')
+
+ rv = ast.Image(loc, name, expr, atl)
+
+ if not l.init:
+ rv = ast.Init(loc, [ rv ], 990)
+
+ l.advance()
+
+ return rv
+
+ ### Define statement.
+ if l.keyword('define'):
+
+ priority = l.integer()
+ if priority:
+ priority = int(priority)
+ else:
+ priority = 0
+
+ name = l.require(l.name)
+ l.require('=')
+ expr = l.rest()
+
+ l.expect_noblock('define statement')
+
+ rv = ast.Define(loc, name, expr)
+
+ if not l.init:
+ rv = ast.Init(loc, [ rv ], priority)
+
+ l.advance()
+
+ return rv
+
+ ### Transform statement.
+ if l.keyword('transform'):
+
+ priority = l.integer()
+ if priority:
+ priority = int(priority)
+ else:
+ priority = 0
+
+ name = l.require(l.name)
+ parameters = parse_parameters(l)
+
+ if parameters and (parameters.extrakw or parameters.extrapos):
+ l.error('transform statement does not take a variable number of parameters')
+
+ l.require(':')
+ l.expect_eol()
+
+ atl = renpy.atl.parse_atl(l.subblock_lexer())
+
+ rv = ast.Transform(loc, name, atl, parameters)
+
+ if not l.init:
+ rv = ast.Init(loc, [ rv ], priority)
+
+ l.advance()
+
+ return rv
+
+ ### One-line python statement.
+ if l.match(r'\$'):
+ python_code = l.rest()
+ l.expect_noblock('one-line python statement')
+ l.advance()
+
+ return ast.Python(loc, python_code)
+
+ ### Python block.
+ if l.keyword('python'):
+
+ hide = False
+ early = False
+
+ if l.keyword('early'):
+ early = True
+
+ if l.keyword('hide'):
+ hide = True
+
+ l.require(':')
+ l.expect_block('python block')
+
+ python_code = l.python_block()
+
+ l.advance()
+
+ if early:
+ return ast.EarlyPython(loc, python_code, hide)
+ else:
+ return ast.Python(loc, python_code, hide)
+
+ ### Label Statement
+ if l.keyword('label'):
+ name = l.require(l.name)
+
+ parameters = parse_parameters(l)
+
+ l.require(':')
+ l.expect_eol()
+
+ # Optional block here. It's empty if no block is associated with
+ # this statement.
+ block = parse_block(l.subblock_lexer())
+
+ l.advance()
+ return ast.Label(loc, name, block, parameters)
+
+ ### Init Statement
+ if l.keyword('init'):
+
+ p = l.integer()
+
+ if p:
+ priority = int(p)
+ else:
+ priority = 0
+
+ if l.keyword('python'):
+
+ hide = False
+ if l.keyword('hide'):
+ hide = True
+
+ l.require(':')
+ l.expect_block('python block')
+
+ python_code = l.python_block()
+
+ l.advance()
+ block = [ ast.Python(loc, python_code, hide) ]
+
+ else:
+ l.require(':')
+
+ l.expect_eol()
+ l.expect_block('init statement')
+
+ block = parse_block(l.subblock_lexer(True))
+
+ l.advance()
+
+ return ast.Init(loc, block, priority)
+
+ # Try parsing as a user-statement. If that doesn't work, revert and
+ # try as a say.
+
+ state = l.checkpoint()
+
+ word = l.word()
+ if (word,) in renpy.statements.registry:
+ text = l.text
+
+ l.expect_noblock(word + ' statement')
+ l.advance()
+
+ renpy.exports.push_error_handler(l.error)
+ try:
+ rv = ast.UserStatement(loc, text)
+ finally:
+ renpy.exports.pop_error_handler()
+
+ return rv
+
+ l.revert(state)
+
+ # Try parsing as the default statement.
+ if () in renpy.statements.registry:
+ text = l.text
+ l.expect_noblock('default statement')
+ l.advance()
+
+ renpy.exports.push_error_handler(l.error)
+ try:
+ rv = ast.UserStatement(loc, text)
+ finally:
+ renpy.exports.pop_error_handler()
+
+ return rv
+
+ # The one and two arguement say statements are ambiguous in terms
+ # of lookahead. So we first try parsing as a one-argument, then a
+ # two-argument.
+
+ # We're using the checkpoint from above.
+
+ what = l.string()
+
+ if l.keyword('with'):
+ with_ = l.require(l.simple_expression)
+ else:
+ with_ = None
+
+ if what is not None and l.eol():
+ # We have a one-argument say statement.
+ l.expect_noblock('say statement')
+ l.advance()
+ return ast.Say(loc, None, what, with_)
+
+ l.revert(state)
+
+ # Try for a two-argument say statement.
+ who = l.simple_expression()
+ what = l.string()
+
+ if l.keyword('with'):
+ with_ = l.require(l.simple_expression)
+ else:
+ with_ = None
+
+ if who and what is not None:
+ l.expect_eol()
+ l.expect_noblock('say statement')
+ l.advance()
+ return ast.Say(loc, who, what, with_)
+
+ l.error('expected statement.')
+
+def parse_block(l):
+ """
+ This parses a block of Ren'Py statements. It returns a list of the
+ statements contained within the block. l is a new Lexer object, for
+ this block.
+ """
+
+ l.advance()
+ rv = [ ]
+
+ while not l.eob:
+ try:
+
+ stmt = parse_statement(l)
+ if isinstance(stmt, list):
+ rv.extend(stmt)
+ else:
+ rv.append(stmt)
+
+ except ParseError as e:
+ parse_errors.append(e.message)
+ l.advance()
+
+ return rv
+
+def parse(fn):
+ """
+ Parses a Ren'Py script contained within the file with the given
+ filename. Returns a list of AST objects representing the
+ statements that were found at the top level of the file.
+ """
+
+ renpy.game.exception_info = 'While parsing ' + fn + '.'
+
+ try:
+ lines = list_logical_lines(fn)
+ nested = group_logical_lines(lines)
+ except ParseError as e:
+ parse_errors.append(e.message)
+ return None
+
+ l = Lexer(nested)
+
+ rv = parse_block(l)
+
+ if parse_errors:
+ return None
+
+ return rv
+
+def report_parse_errors():
+
+ if not parse_errors:
+ return False
+
+ erfile = "errors.txt"
+ f = file(erfile, "w")
+ opat_err = os.path.realpath(erfile)
+ f.write(codecs.BOM_UTF8)
+
+ print("I'm sorry, but errors were detected in your script. Please correct the", file=f)
+ print("errors listed below, and try again.", file=f)
+ print(file=f)
+
+ for i in parse_errors:
+
+ try:
+ i = i.encode("utf-8")
+ except:
+ pass
+
+ print()
+ print(file=f)
+ print(i)
+ print(i, file=f)
+
+ print(file=f)
+ print("Ren'Py Version:", renpy.version, file=f)
+
+ f.close()
+
+ try:
+ if renpy.config.editor:
+ renpy.exports.launch_editor([ opat_err ], 1, transient=1)
+ else:
+ os.startfile(erfile) # E1101
+ except:
+ pass
+
+ return True
+
+
diff --git a/ast2json/renpy/statements.py b/ast2json/renpy/statements.py
new file mode 100644
index 0000000..3acd950
--- /dev/null
+++ b/ast2json/renpy/statements.py
@@ -0,0 +1,686 @@
+# Copyright 2004-2010 PyTom <pytom@bishoujo.us>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# This module contains code to support user-defined statements.
+
+import renpy
+
+# The statement registry. It's a map from tuples giving the prefixes of
+# statements to dictionaries giving the methods used for that statement.
+registry = { }
+
+def register(name, parse=None, lint=None, execute=None, predict=None, next=None, scry=None):
+
+ if name == "":
+ name = ()
+ else:
+ name = tuple(name.split())
+
+ registry[name] = dict(parse=parse,
+ lint=lint,
+ execute=execute,
+ predict=predict,
+ next=next,
+ scry=scry)
+
+ while True:
+ name = name[:-1]
+ if not name:
+ break
+
+ if name not in registry:
+ registry[name] = None
+
+def parse(node, line):
+
+ block = [ (node.filename, node.linenumber, line, [ ]) ]
+ l = renpy.parser.Lexer(block)
+ l.advance()
+
+ name = ()
+
+ while True:
+ cpt = l.checkpoint()
+ word = l.word()
+
+ if word is None:
+ break
+
+ newname = name + (word,)
+
+ if newname not in registry:
+ break
+
+ name = newname
+
+ l.revert(cpt)
+
+ if registry[name] is None:
+ return None
+
+ return ( name, registry[name]["parse"](l) )
+
+
+def call(method, parsed, *args, **kwargs):
+ name, parsed = parsed
+
+ method = registry[name].get(method)
+ if method is None:
+ return None
+
+ return method(parsed, *args, **kwargs)
+
+# Music play - The example of a full statement.
+
+def parse_play_music(l):
+
+
+ file = l.simple_expression()
+ if not file:
+ renpy.error("play requires a file")
+
+ fadeout = "None"
+ fadein = "0"
+ channel = None
+ loop = None
+ if_changed = False
+
+ while True:
+
+ if l.eol():
+ break
+
+ if l.keyword('fadeout'):
+ fadeout = l.simple_expression()
+ if fadeout is None:
+ renpy.error('expected simple expression')
+
+ continue
+
+ if l.keyword('fadein'):
+ fadein = l.simple_expression()
+ if fadein is None:
+ renpy.error('expected simple expression')
+
+ continue
+
+ if l.keyword('channel'):
+ channel = l.simple_expression()
+ if channel is None:
+ renpy.error('expected simple expression')
+
+ continue
+
+ if l.keyword('loop'):
+ loop = True
+ continue
+
+ if l.keyword('noloop'):
+ loop = False
+ continue
+
+ if l.keyword('if_changed'):
+ if_changed = True
+ continue
+
+ renpy.error('could not parse statement.')
+
+ return dict(file=file,
+ fadeout=fadeout,
+ fadein=fadein,
+ channel=channel,
+ loop=loop,
+ if_changed=if_changed)
+
+def execute_play_music(p):
+
+ if p["channel"] is not None:
+ channel = eval(p["channel"])
+ else:
+ channel = "music"
+
+ renpy.music.play(eval(p["file"]),
+ fadeout=eval(p["fadeout"]),
+ fadein=eval(p["fadein"]),
+ channel=channel,
+ loop=p.get("loop", None),
+ if_changed=p.get("if_changed", False))
+
+def predict_play_music(p):
+ return [ ]
+
+def lint_play_music(p, channel="music"):
+
+ file = _try_eval(p["file"], 'filename')
+
+ if p["channel"] is not None:
+ channel = _try_eval(p["channel"], 'channel')
+
+ if not isinstance(file, list):
+ file = [ file ]
+
+ for fn in file:
+ if isinstance(fn, basestring):
+ try:
+ if not renpy.music.playable(fn, channel):
+ renpy.error("%r is not loadable" % fn)
+ except:
+ pass
+
+register('play music',
+ parse=parse_play_music,
+ execute=execute_play_music,
+ predict=predict_play_music,
+ lint=lint_play_music)
+
+# From here on, we'll steal bits of other statements when defining other
+# statements.
+
+def parse_queue_music(l):
+
+ file = l.simple_expression()
+ if not file:
+ renpy.error("queue requires a file")
+
+ channel = None
+ loop = None
+
+ while not l.eol():
+
+ if l.keyword('channel'):
+ channel = l.simple_expression()
+ if channel is None:
+ renpy.error('expected simple expression')
+
+ if l.keyword('loop'):
+ loop = True
+ continue
+
+ if l.keyword('noloop'):
+ loop = False
+ continue
+
+ renpy.error('expected end of line')
+
+ return dict(file=file, channel=channel, loop=loop)
+
+def execute_queue_music(p):
+ if p["channel"] is not None:
+ channel = eval(p["channel"])
+ else:
+ channel = "music"
+
+ renpy.music.queue(
+ eval(p["file"]),
+ channel=channel,
+ loop=p.get("loop", None))
+
+
+register('queue music',
+ parse=parse_queue_music,
+ execute=execute_queue_music,
+ lint=lint_play_music)
+
+def parse_stop_music(l):
+ fadeout = "None"
+
+ if l.keyword("fadeout"):
+ fadeout = l.simple_expression()
+
+ channel = None
+
+ if l.keyword('channel'):
+ channel = l.simple_expression()
+ if channel is None:
+ renpy.error('expected simple expression')
+
+ if not l.eol():
+ renpy.error('expected end of line')
+
+ if fadeout is None:
+ renpy.error('expected simple expression')
+
+ return dict(fadeout=fadeout, channel=channel)
+
+def execute_stop_music(p):
+ if p["channel"] is not None:
+ channel = eval(p["channel"])
+ else:
+ channel = "music"
+
+ renpy.music.stop(fadeout=eval(p["fadeout"]), channel=channel)
+
+register('stop music',
+ parse=parse_stop_music,
+ execute=execute_stop_music)
+
+
+# Sound statements. They share alot with the equivalent music
+# statements.
+
+def execute_play_sound(p):
+
+ if p["channel"] is not None:
+ channel = eval(p["channel"])
+ else:
+ channel = "sound"
+
+ fadeout = eval(p["fadeout"]) or 0
+
+ renpy.sound.play(eval(p["file"]),
+ fadeout=fadeout,
+ fadein=eval(p["fadein"]),
+ channel=channel)
+
+def lint_play_sound(p, lint_play_music=lint_play_music):
+ return lint_play_music(p, channel="sound")
+
+register('play sound',
+ parse=parse_play_music,
+ execute=execute_play_sound,
+ lint=lint_play_sound)
+
+def execute_queue_sound(p):
+ if p["channel"] is not None:
+ channel = eval(p["channel"])
+ else:
+ channel = "sound"
+
+ renpy.sound.queue(eval(p["file"]), channel=channel)
+
+
+register('queue sound',
+ parse=parse_queue_music,
+ execute=execute_queue_sound,
+ lint=lint_play_music)
+
+def execute_stop_sound(p):
+ if p["channel"] is not None:
+ channel = eval(p["channel"])
+ else:
+ channel = "sound"
+
+ fadeout = eval(p["fadeout"]) or 0
+
+ renpy.sound.stop(fadeout=fadeout, channel=channel)
+
+register('stop sound',
+ parse=parse_stop_music,
+ execute=execute_stop_sound)
+
+
+# Generic play/queue/stop statements. These take a channel name as
+# the second thing.
+
+def parse_play_generic(l, parse_play_music=parse_play_music):
+ channel = l.name()
+
+ if channel is None:
+ renpy.error('play requires a channel')
+
+ rv = parse_play_music(l)
+ if rv["channel"] is None:
+ rv["channel"] = repr(channel)
+
+ return rv
+
+def parse_queue_generic(l, parse_queue_music=parse_queue_music):
+ channel = l.name()
+
+ if channel is None:
+ renpy.error('queue requires a channel')
+
+ rv = parse_queue_music(l)
+ if rv["channel"] is None:
+ rv["channel"] = repr(channel)
+
+ return rv
+
+def parse_stop_generic(l, parse_stop_music=parse_stop_music):
+ channel = l.name()
+
+ if channel is None:
+ renpy.error('stop requires a channel')
+
+ rv = parse_stop_music(l)
+ if rv["channel"] is None:
+ rv["channel"] = repr(channel)
+
+ return rv
+
+def lint_play_generic(p, lint_play_music=lint_play_music):
+ channel = eval(p["channel"])
+
+ if not renpy.music.channel_defined(channel):
+ renpy.error("channel %r is not defined" % channel)
+
+ lint_play_music(p, channel)
+
+def lint_stop_generic(p):
+ channel = eval(p["channel"])
+
+ if not renpy.music.channel_defined(channel):
+ renpy.error("channel %r is not defined" % channel)
+
+register('play',
+ parse=parse_play_generic,
+ execute=execute_play_music,
+ predict=predict_play_music,
+ lint=lint_play_generic)
+
+register('queue',
+ parse=parse_queue_generic,
+ execute=execute_queue_music,
+ lint=lint_play_generic)
+
+register('stop',
+ parse=parse_stop_generic,
+ execute=execute_stop_music,
+ lint=lint_stop_generic)
+
+
+##########################################################################
+# "window show" and "window hide" statements.
+
+def parse_window(l):
+ p = l.simple_expression()
+ if not l.eol():
+ renpy.error('expected end of line')
+
+ return p
+
+def lint_window(p):
+ if p is not None:
+ _try_eval(p, 'window transition')
+
+def execute_window_show(p):
+ if store._window:
+ return
+
+ if p is not None:
+ trans = eval(p)
+
+ renpy.with_statement(None)
+ store._window = True
+ renpy.with_statement(trans)
+
+def execute_window_hide(p):
+ if not _window:
+ return
+
+ if p is not None:
+ trans = eval(p)
+
+ renpy.with_statement(None)
+ store._window = False
+ renpy.with_statement(trans)
+
+register('window show',
+ parse=parse_window,
+ execute=execute_window_show,
+ lint=lint_window)
+
+register('window hide',
+ parse=parse_window,
+ execute=execute_window_hide,
+ lint=lint_window)
+
+##########################################################################
+# Pause statement.
+
+def parse_pause(l):
+
+ delay = l.simple_expression()
+
+ if not l.eol():
+ renpy.error("expected end of line.")
+
+ return { "delay" : delay }
+
+def lint_pause(p):
+
+ if p["delay"]:
+ _try_eval(p["delay"], 'pause statement')
+
+def execute_pause(p):
+
+ if p["delay"]:
+ delay = eval(p["delay"])
+ renpy.with_statement(Pause(delay))
+ else:
+ renpy.pause()
+
+
+register('pause',
+ parse=parse_pause,
+ lint=lint_pause,
+ execute=execute_pause)
+
+
+
+def _try_eval(e, what):
+ try:
+ return eval(e)
+ except:
+ renpy.error('unable to evaluate %s %r' % (what, e))
+
+##############################################################################
+# Screen-related statements.
+
+def parse_show_call_screen(l):
+
+ # Parse a name.
+ name = l.require(l.name)
+
+ # Parse the list of arguments.
+ arguments = renpy.parser.parse_arguments(l)
+ l.expect_eol()
+
+ return dict(name=name, arguments=arguments)
+
+def parse_hide_screen(l):
+ name = l.require(l.name)
+
+ l.expect_eol()
+
+ return dict(name=name)
+
+def predict_screen(p):
+ if not p["arguments"]:
+ renpy.predict_screen(p["arguments"])
+
+def execute_show_screen(p):
+
+ name = p["name"]
+ a = p["arguments"]
+
+ args = [ ]
+ kwargs = { }
+
+ if a is not None:
+
+ for k, v in a.arguments:
+ if k is not None:
+ kwargs[k] = eval(v)
+ else:
+ args.append(eval(v))
+
+ if a.extrapos is not None:
+ args.extend(eval(a.extrapos))
+
+ if a.extrakw is not None:
+ kwargs.update(eval(a.extrakw))
+
+ renpy.show_screen(name, *args, **kwargs)
+
+def execute_call_screen(p):
+ name = p["name"]
+ a = p["arguments"]
+
+ args = [ ]
+ kwargs = { }
+
+ if a is not None:
+
+ for k, v in a.arguments:
+ if k is not None:
+ kwargs[k] = eval(v)
+ else:
+ args.append(eval(v))
+
+ if a.extrapos is not None:
+ args.extend(eval(a.extrapos))
+
+ if a.extrakw is not None:
+ kwargs.update(eval(a.extrakw))
+
+ store._return = renpy.call_screen(name, *args, **kwargs)
+
+def execute_hide_screen(p):
+ name = p["name"]
+ renpy.hide_screen(name)
+
+def lint_screen(p):
+ name = p["name"]
+ if not renpy.has_screen(name):
+ renpy.error("Screen %s does not exist." % name)
+
+
+register("show screen",
+ parse=parse_show_call_screen,
+ execute=execute_show_screen,
+ predict=predict_screen,
+ lint=lint_screen)
+
+register("call screen",
+ parse=parse_show_call_screen,
+ execute=execute_call_screen,
+ predict=predict_screen,
+ lint=lint_screen)
+
+register("hide screen",
+ parse=parse_hide_screen,
+ execute=execute_hide_screen)
+
+
+
+def parse_jump_in(l):
+ is_expr = False
+ firstword = l.word()
+ if firstword == "expression":
+ label = l.simple_expression()
+ is_expr = True
+ else:
+ label = firstword
+ if not label:
+ renpy.error("parse error when evaluating custom jump")
+ return dict(label=label,is_expr=is_expr)
+
+def execute_jump_in(p):
+ global save_name, last_scene_label
+ if p["is_expr"]:
+ label = eval(p["label"])
+ else:
+ label = p["label"]
+ last_scene_label = label
+ save_name = label
+ scene_register(label)
+ renpy.jump(label)
+
+def predict_jump_in(p):
+ return []
+
+def next_jump_in(p):
+ return p["label"]
+
+def lint_jump_in(p):
+ label = p["label"]
+ if not label:
+ renpy.error("no target given to custom jump statement.")
+ if not renpy.has_label(label) and not p["is_expr"]:
+ renpy.error("custom jump to nonexistent label.")
+
+def execute_jump_out(p):
+ global playthroughflag, mycontext, ss_desc
+ nvl_clear()
+ if not playthroughflag:
+ replay_end()
+ execute_jump_in(p)
+
+register('jump_in',
+ parse=parse_jump_in,
+ execute=execute_jump_in,
+ predict=predict_jump_in,
+ lint=lint_jump_in,
+ next=next_jump_in)
+
+register('jump_out',
+ parse=parse_jump_in,
+ execute=execute_jump_out,
+ predict=predict_jump_in,
+ lint=lint_jump_in,
+ next=next_jump_in)
+def parse_nvl_show_hide(l):
+ rv = l.simple_expression()
+ if rv is None:
+ renpy.error('expected simple expression')
+
+ if not l.eol():
+ renpy.error('expected end of line')
+
+ return rv
+
+def lint_nvl_show_hide(trans):
+ _try_eval(trans, 'transition')
+
+def execute_nvl_show(trans):
+ nvl_show(eval(trans))
+
+def execute_nvl_hide(trans):
+ nvl_hide(eval(trans))
+
+register("nvl show",
+ parse=parse_nvl_show_hide,
+ execute=execute_nvl_show,
+ lint=lint_nvl_show_hide)
+
+register("nvl hide",
+ parse=parse_nvl_show_hide,
+ execute=execute_nvl_hide,
+ lint=lint_nvl_show_hide)
+
+def parse_nvl_clear(l):
+ if not l.eol():
+ renpy.error('expected end of line')
+
+ return None
+
+def execute_nvl_clear(parse):
+ nvl_clear()
+
+def scry_nvl_clear(parse, scry):
+ scry.nvl_clear = True
+
+register('nvl clear',
+ parse=parse_nvl_clear,
+ execute=execute_nvl_clear,
+ scry=scry_nvl_clear)
+
diff --git a/ast2json/rpyc2json.py b/ast2json/rpyc2json.py
index 1bf9b73..b1dc592 100755
--- a/ast2json/rpyc2json.py
+++ b/ast2json/rpyc2json.py
@@ -42,6 +42,8 @@ import renpy.game
renpy.game.script = Dummy()
import renpy.ast
import renpy.atl
+import renpy.statements
+import renpy.parser
def pretty_print_ast(out_file, ast):
json.dump(rast2json(ast), out_file, separators=(',', ':'))
@@ -49,6 +51,8 @@ def pretty_print_ast(out_file, ast):
def node2json(node):
to_return = {}
to_return['_type'] = node.__class__.__name__
+ if isinstance(node, renpy.ast.UserStatement):
+ node.parsed = renpy.statements.parse(node, node.line)
for attr in node.__slots__:
to_return[attr] = get_value(getattr(node, attr))
@@ -64,6 +68,8 @@ def get_value(attr_value):
return attr_value
if isinstance(attr_value, list) or isinstance(attr_value, tuple):
return [get_value(x) for x in attr_value]
+ if isinstance(attr_value, dict):
+ return attr_value
if isinstance(attr_value, renpy.ast.Node):
return node2json(attr_value)
if isinstance(attr_value, renpy.ast.PyCode):
diff --git a/www/js/imachine.js b/www/js/imachine.js
index 2fb8922..694aea5 100644
--- a/www/js/imachine.js
+++ b/www/js/imachine.js
@@ -15,88 +15,33 @@ html5ks.imachine = {
i = 0,
runInst = function () {
var inst = cmds[i++];
- switch (typeof inst) {
- case "undefined":
- deferred.resolve();
+ switch (inst._type) {
+ case 'Call':
+ switch (inst.label) {
+ case "act_op":
+ case "iscene":
+ html5ks.api[inst.label].call(html5ks.api, inst['arguments'][0][0][1]).then(runInst, deferred.reject);
+ default:
+ throw new Error('unknown Call label');
+ }
break;
- case "object":
- var cmd = inst[0];
- var args = inst.slice(1);
- switch (inst[0]) {
+ case 'UserStatement':
+ inst = inst.parsed;
+ switch (inst[0][0]) {
case "jump_out":
- var newlabel = args[0];
- if (newlabel === "restart") {
- html5ks.menu.mainMenu();
- } else if (!html5ks.data.imachine[newlabel]) {
- deferred.reject(new Error("label does not exist"));
- } else {
- this.run(newlabel);
- }
- break;
- case "iscene":
- this.scene_register(inst[1]);
+ html5ks.api.nvl("clear");
/* falls through */
- case "act_op":
- switch (inst[1]) {
- case "op_vid1":
- html5ks.api.movie_cutscene("op_1").then(runInst, deferred.reject);
- break;
- default:
- html5ks.api[cmd].apply(html5ks.api, args).then(runInst, deferred.reject);
- }
- break;
- case "imenu":
- html5ks.api.iscene(args[0]).then(function (choice) {
- this._return = choice;
- runInst();
- }.bind(this), console.error);
- break;
- case "if":
- var cpy = inst.slice(0),
- type = '',
- next = null;
- el: while ((type = cpy.shift())) {
- switch (type) {
- case "if":
- case "elif":
- var cond = cpy.shift();
- next = cpy.shift();
- switch (cond[0]) {
- case "_return":
- if (this._return == cond[1]) {
- break el;
- }
- break;
- case "seen_scene":
- if (this.seen_scene(cond[1])) {
- break el;
- }
- break;
- case "attraction_sc":
- case "attraction_hanako":
- case "attraction_kenji":
- if (html5ks.store.attraction[cond[0]] > cond[1]) {
- break el;
- }
- break;
- default:
- throw new Error("unhandled if statement");
- }
- break;
- case "else":
- next = cpy.shift();
- break el;
- }
- }
- return html5ks.imachine.run(next).then(runInst, console.error);
- case "path_end":
- console.error("TODO: disp vid + add to persistent, args:");
- console.log(args);
- deferred.resolve();
- break;
+ case "jump_in":
+ return this.run(inst[1].label);
default:
- deferred.reject(new Error("unknown imachine inst: " + inst));
+ throw new Error('not implemented');
}
+ case 'If':
+ throw new Error('not implemented');
+ case 'Pass':
+ break;
+ default:
+ throw new Error('unknown imachine inst');
}
}.bind(this);
runInst();
diff --git a/www/js/menu.js b/www/js/menu.js
index eb87111..93616db 100644
--- a/www/js/menu.js
+++ b/www/js/menu.js
@@ -168,21 +168,14 @@ html5ks.menu = {
this.initEvents();
this.initOptions();
- when.all([html5ks.fetch("json", "imachine"),
- html5ks.fetch("json", "script").then(function (d) {
- for (var k in d) {
- if (k.slice(0, 3) === (html5ks.persistent.language === "en" ? "fr_" : "en_")) {
- delete d[k];
- }
- }
- }, console.error)]).then(function () {
- var start = this.elements.main.start;
- start.addEventListener("click", function () {
- this.elements.mainMenu.style.display = "none";
- html5ks.imachine.start().then(this.mainMenu.bind(this), console.error);
- }.bind(this), false);
- start.classList.remove("disabled");
- }.bind(this), console.error);
+ html5ks.fetch("json", "imachine").then(function () {
+ var start = this.elements.main.start;
+ start.addEventListener("click", function () {
+ this.elements.mainMenu.style.display = "none";
+ html5ks.imachine.start().then(this.mainMenu.bind(this), console.error);
+ }.bind(this), false);
+ start.classList.remove("disabled");
+ }.bind(this), console.error);
},
context: function (show) {