I'd like to be able to use renpy-like scripts in games not run by renpy. For this reason I am looking into renpys code to see how tightly the parser for renpy scripts is interwoven with the rest of the engine.
This is what I got so far:
The sequence of code called while renpy is launched that leads to loading and parsing the renpy script files (.rpy) in very broad strokes:
# error handling loop
renpy.bootstrap.bootstrap()
# main function called from error handling loop
renpy.main.main()
# load all .rpy files, which in their entirety together form "the script"
renpy.script.Script()
"""
This class represents a Ren'Py script, which is parsed out of a
collection of script files. Once parsing and initial analysis is
complete, this object can be serialized out and loaded back in,
so it shouldn't change at all after that has happened.
@ivar namemap: A map from the name of an AST node to the AST node
itself. This is used for jumps, calls, and to find the current
node when loading back in a save. The names may be strings or
integers, strings being explicit names provided by the user, and
integers being names synthesised by renpy.
@ivar initcode: A list of priority, Node tuples that should be
executed in ascending priority order at init time.
@ivar all_stmts: A list of all statements, that have been found
in every file. Useful for lint, but tossed if lint is not performed
to save memory.
"""
AST:
http://en.wikipedia.org/wiki/Abstract_syntax_treeAll .rpy files are parsed at some point and their contents is represented by the Script class. Comments in renpy code calls this "the script".
"The script" is divided into nodes in an abstract syntax tree. Those nodes are defined in the renpy.ast module.
renpy.translation.Translator can sort nodes that require different treatment into different attributes. For example, python nodes go into the Translator.python dict and string nodes usable with StringTranslator go into the Translator.strings dict.
# find all files that will collectively form the script
renpy.script.Script.scan_script_files()
.rpy and .rpyc files go into renpy.script.Script.script_files
.rpym and .rpymc go into renpy.script.Script.module_files
# from renpy.script.Script.__init__ call Translator.chain_translates
As far as I can tell, Translator.chain_translates will do nothing because it "loops" over Translator.chain_worklist, which is initialized to an empty list. Translator.chain_worklist is filled when Script.load_script runs, which is obviously after the constructor of Script is done.
# from renpy.main.main call renpy.scrip.Script.load_script()
for each .rpy and .rpyc file:
Run it through a series of loading methods, ending up at Script.load_file_core,
which calls renpy.parser.parse
# the parser for the renpy script language: renpy.parser
# This module contains the parser for the Ren'Py script language. It's
# called when parsing is necessary, and creates an AST from the script.
The parser module does not import anything not in Pythons stdlib directly. It uses some parts of renpy 23 times by calling renpy.<something> (7 times renpy.ast and 4 times renpy.atl, which we probably need anyway; 4 times renpy.config, which is easy to replace).
Bottom line: renpy.parser seems easy to extract if renpy.ast and renpy.atl are easy to extract too, which I have not looked into yet.
Update1:
renpy.ast contains the classes implementing all the statements of renpys scripting language. Several of these statements make good use of objects outside of the renpy.ast module. In total, there are 130 calls to renpy.<something>, most of which we would have to replace (not all because we probably don't need all statements).
renpy.atl refers to the Ren'Py Animation and Transformation language. Since that is specific to the GUI toolkit used, I don't think we need it, at least not for the initial version of our event scripting language.
In conclusion, it appears as if renpy.parser and renpy.ast contain most of the code we need. Taking that, removing everything we don't need and replacing all calls to renpys infrastructure with a simpler, generic environment is IMHO likely to be faster than implementing something equally powerful on our own. The renpy scripting language has been around for a while and received its share of bugfixes and performance improvements. I think we could benefit from that.