feat: converter reads the Phone library's register statement, named stores and generators
`init phone register` becomes a default (the group chat) plus a function of library calls that runs with the other defaults of a new game (phone_register.py). `init/python [early|hide] in some.store` wraps its code in an in_store node and `default some.store.name` may name a store; yield works as an expression (generator functions are marked gen); `import x as y`, `transform -150 name(...)`, `init:` with if/else, `config.special_namespaces` lines, `at` lists and tuples, transforms made by Python functions, `choice` weights that are expressions, `__len__`/`__bool__`, and the whitelist for os, calendar, itertools, pygame_sdl2 and the new runtime. The converter also records the size of every picture it converts (`imageSizes`) so `renpy.image_size` answers without loading. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e94f9b0d9e
commit
83cc7fdd5c
11 files changed
+379
-26
No files matched your search
@@ -25,7 +25,7 @@ UNARY_OPS = {ast.Not: "not", ast.USub: "-", ast.UAdd: "+", ast.Invert: "~"}
|
||||
|
||||
# Modules whose import is harmless: the player provides the functions it supports.
|
||||
KNOWN_MODULES = {"random", "math", "renpy", "store", "time", "datetime", "re", "copy", "collections",
|
||||
"itertools", "string", "functools", "operator", "json", "gc", "hashlib", "uuid", "pygame", "colorsys", "fnmatch", "traceback", "builtins"}
|
||||
"itertools", "string", "functools", "operator", "json", "gc", "hashlib", "uuid", "pygame", "colorsys", "fnmatch", "traceback", "builtins", "os", "calendar", "pygame_sdl2"}
|
||||
|
||||
|
||||
class Unsupported(Exception):
|
||||
@@ -146,6 +146,8 @@ def expr(node: ast.AST) -> Expr:
|
||||
"r": [expr(r) for r in rs]}
|
||||
case ast.IfExp(test=t, body=b, orelse=e):
|
||||
return {"k": "if", "c": expr(t), "t": expr(b), "e": expr(e)}
|
||||
case ast.Yield(value=value): # `x = yield` and `x = yield v`: the value sent in by `.send(...)`
|
||||
return {"k": "yield", "v": None if value is None else expr(value)}
|
||||
case ast.List(elts=elts) | ast.Set(elts=elts):
|
||||
return {"k": "list", "v": [element(e) for e in elts]}
|
||||
case ast.Tuple(elts=elts):
|
||||
@@ -282,7 +284,7 @@ def stmt(node: ast.stmt) -> list[Stmt]:
|
||||
# Pickle hooks only matter for Ren'Py's own save files; the player keeps its own
|
||||
IGNORED_DUNDERS = {"__setstate__", "__getstate__", "__reduce__", "__reduce_ex__"}
|
||||
SUPPORTED_DUNDERS = {"__init__", "__str__", "__repr__", "__call__", "__eq__", "__hash__", "__iter__", "__getitem__",
|
||||
"__contains__"}
|
||||
"__contains__", "__len__", "__bool__"}
|
||||
INACTIVE_VARIANTS = {"gui.variant", "renpy.variant"}
|
||||
TRANSPARENT_DECORATORS = {"renpy.pure"} # marks a function as side-effect free; no runtime meaning
|
||||
DECORATORS = {"staticmethod", "classmethod", "property"}
|
||||
@@ -314,18 +316,17 @@ def decorator(node: ast.expr) -> Expr:
|
||||
|
||||
|
||||
def is_generator(body: list[ast.stmt]) -> bool:
|
||||
"""A function with `yield` statements. The player runs it to the end and hands out what it yielded, which is
|
||||
right for generators that end; `x = yield` (sending values in) and async code stay unsupported."""
|
||||
"""A function with `yield` in it: the player runs it step by step (next, send). `yield from` is accepted as a
|
||||
statement only, and async code stays unsupported."""
|
||||
module = ast.Module(body=body, type_ignores=[])
|
||||
statement_yields = {id(n.value) for n in ast.walk(module)
|
||||
if isinstance(n, ast.Expr) and isinstance(n.value, (ast.Yield, ast.YieldFrom))}
|
||||
statement_froms = {id(n.value) for n in ast.walk(module) if isinstance(n, ast.Expr) and isinstance(n.value, ast.YieldFrom)}
|
||||
found = False
|
||||
for n in ast.walk(module):
|
||||
if isinstance(n, ast.Await):
|
||||
raise Unsupported("async")
|
||||
if isinstance(n, ast.YieldFrom) and id(n) not in statement_froms:
|
||||
raise Unsupported("yield from как выражение")
|
||||
if isinstance(n, (ast.Yield, ast.YieldFrom)):
|
||||
if id(n) not in statement_yields:
|
||||
raise Unsupported("yield как выражение")
|
||||
found = True
|
||||
return found
|
||||
|
||||
|
||||
Reference in new issue
Block a user