feat: support user-defined functions and classes in Ren'Py python

- Converter: def/return/global/nonlocal, class (inheritance, property,
  staticmethod, classmethod), lambda, *args/**kwargs and call-site unpacking.
  Unsupported dunder methods, generators and unknown decorators are still refused
- Player: closures, super(), instances that survive save/load, isinstance,
  hasattr/getattr/setattr, map/filter/sorted(key=), recursion limit
- audit_ir accepts calls to names, classes and methods the game defines itself;
  unknown renpy.* calls still block the import. Volume/pan/pause/replay calls
  are accepted as presentation-only no-ops; build.* settings are skipped
- Tests: IR fixture compiled from tests/fixtures/classes.py (tests/make_ir.py)
  and run in the interpreter; converter tests updated

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Sonnet 5.5 committed 2026-10-25 12:01:09 +03:00
1 parent 12ec82c7e6
commit 962595c925
12 files changed
+782 -23

No files matched your search

+2 -2
View File
@@ -1,7 +1,7 @@
// PGVN interpreter + in-game UI.
import { AudioSystem } from "./audio";
import { JumpSignal, PyDict, Python, decodeValue, encodeValue, pyStr, truthy, type Scope } from "./python";
import { JumpSignal, PyDict, Python, decodeValue, encodeValue, pyStr, truthy, userBuiltins, type Scope } from "./python";
import { Renderer, type ResolvedImage } from "./renderer";
import { fastPoint, interpolate, parseText, plainText, renderText, waitPoints } from "./text";
import { languageName, rememberLanguage, type Translator } from "./i18n";
@@ -352,9 +352,9 @@ export class Engine {
reversed: (xs: any[]) => [...xs].reverse(),
enumerate: (xs: any[]) => [...xs].map((x, i) => [i, x]),
zip: (...lists: any[][]) => lists[0].map((_, i) => lists.map((l) => l[i])),
isinstance: () => false,
_: (s: string) => s,
__: (s: string) => this.tr.str(s),
...userBuiltins(this.py),
};
}