RTTI Script alpha release
Way back at the start of this year, I wrote about how I’d been working on a new Object Pascal-based script engine built around Delphi’s extended RTTI system. And then it got real quiet, because more immediate concerns took priority for me. But I’ve gotten to the point in my game engine development where I really need to put in the scripting system, so I spent the last couple weeks finishing up the implementation and tuning the performance a little, and now I’ve got an alpha version ready. If anyone would like to try it out, they can find it on Google Code.
Note: This is an alpha version. There are probably plenty of bugs in it. There are still features I haven’t gotten around to implementing. The sight of the code would probably send Barry Kelly fleeing from the room, screaming in horror. So use at your own risk, and I take no responsibility if this causes your system to crash, your data to come out all wrong, or demons to fly out your nose. Specifically, the program keyword is not implemented yet, (only units,) var parameters don’t work yet, and a handful of features aren’t implemented in the script executor.
Having said that, here’s a few cool features I’ve come up with:
- Class creation: Defining a new class inside the script code is not supported yet, but the basic framework is there, and script units implement new classes with their routines as methods, accessible to the script executor through the RTTI system. I’ll add class creation pretty soon.
- Automatic registration: Ever try to register a class with the script engine in PascalScript? It’s a painful process involving generating a registration unit that basically redeclares your entire class, and then has to be kept in sync or things break. (And if you get the dependency order wrong, it fails silently, leading to weird errors further down the line.) I figured that was way too complicated, so for RTTIScript, all you have to do is register a class reference, and it uses the RTTI system to figure out the internal members and dependencies for you.
- Execution environment: You’ll often have a bunch of little standalone routines and global variables that you want to import into a script to have available at all times, to provide an interface between the script and your program. So I introduced the concept of the “environment:” You create a class and register it with the compiler, and it treats all the class’s public methods and properties as globals for the script. (This also means that you can use property getters and setters to have the script’s interaction with its “global variables” invoke side effects.)
- Script saving: I know Eric Grange thinks that a fast compiler is good enough and a bytecode version of a script is not necessary, but I don’t agree. By his own tests, even a very fast script compiler can take 2 seconds to compile a 100,000 line script. I’ve actually got an example of one that big in a project that the author wants to convert over once the TURBU project is ready. Adding a two-second lag into the loading process and making the player wait while a level loads, which should feel instantaneous, is unacceptable. I don’t have script saving implemented yet, but it’s definitely on the to-do list.
Anyway, the alpha version is available for any guinea pigs curious developers out there who’d like to take a look at it. It requires Delphi XE, it’s pretty fast, and when I get the rest of the features set up it’s going to be awesome! Feel free to check it out, and to submit a bug report if you find some errors. (Which I’m sure plenty of people will.)
I’ll need to redo that bench, should be quite less than 2 seconds now, and DWS 2.3 should be even faster 🙂
That said, while loading a game level, you’ll be I/O bound, meaning you can compile in a background thread, while other assets load.
With DWS you can even have script-driven resource loading happen while that same script that loads them is still getting compiled (need to write about it someday, compiler has some referential transparency capability usually found only in functional languages).
With RTTI you’ll not be able to place everything in the bytecode since some pointers change from build to build.
Actually, the way I’ve got it set up, loading a game level involves a minimal amount of IO. It’s basically CPU-bound and is very, very fast. The only thing in my current setup that takes any noticeable amount of time is preparing the scripts, which is why I’m working on a way to save pre-compiled scripts. And yes, of course the RTTI pointers will change, but I can add fixups for that easily enough.
Redid the test: on the 100k line test case, it compiles in 0.83 seconds now.
Also those are some very dense 100k lines (4.3 MB of source, no comments, no blanks, all long lines), when “normalized” to “Classes.pas” code density, they’re equivalent to 167k lines, so on regular formatted code, 100k lines should compile in just under half a second.
Very interesting. Is there an example of how to use it?
+1 with Frank
Tried using TrsCompiler directly, but couldn’t get anything to compile: isolated code fails on first variable or procedure declaration, units fails with “statement expected”.
A sample ‘hello world!’ application would be helpful to get started indeed.
Very nice code.
Perhaps you may be able to make execution even faster by using inline for some methods, e.g. TrsVM.GetValue() and most of the TrsVM.*() opcodes implementation.
Is this project dead??
Yeah, basically. It turns out DWS is a much better script engine than I’d ever be able to write, and Eric’s given me commit access to the project so I could add a few features. I’m maintaining RTTI Script a little because there are a few Delphi compiler bugs keeping me from implementing DWS in the TURBU project, but I think they’ll all be resolved in XE5, and at that point I’m probably going to transition over to DWS and just abandon this project.