How would you design your own ideal scripting language? Would you go with a functional language in the LISP family, or with a more procedural style? Would you offer object oriented organization? Would large parts of your ideal language be recognizable as C or another common language, or would you "go for broke" with a domain specific language that (probably) only you will be able to read?
One of the fun things about computer science is that people will answer this question in very different ways, and they can all be right. Almost everything (including your dog) is probably Turing complete. And since the ultimate goal is accomplishing whatever operations the script is performing, whatever helps you express that well is the right answer, for you.
When implementing my cross platform graphical app engine, a set of characteristics began to crystalize for my ideal scripting language:
My scripting language wish list
- Syntax recognizable as C-derived, I guess similar to C#.
- Object-based encapsulation of logic such as you’d find with C#.
- Strong, static, compile time type checking, like C#.
- Easy closures, very much as are provided by C#.
- Darnit, I just want C#!
It may surprise you to learn that as a result of this mature and sophisticated thought process, I started researching how I could possibly just use C# as my scripting language. While I use Python for general environment/command line scripting needs in my personal projects, I have sometimes chosen C#. It’s under-appreciated as a scripting language, and in fact it’s pretty easy to write your own C# compiler using the services exposed by either Mcrosoft or Mono.
One obvious approach is that the C# code could just be normally compiled into each app. I rejected that approach, though, because I want my engine to allow for providing dynamic updates of arbitrary content without requiring the user to go through the app store for upgrades.
Walled Gardens
It turns out that this is the biggest constraint for me — on a walled garden platform like iOS or Windows 8 Store apps, you can’t deploy new executable binaries without going through the official store approval process.
There’s a really nifty Microsoft .NET namespace called "System.Reflection.Emit" providing classes to generate bytecode at runtime. Unfortunately, it’s not available with Xamarin’s C# compiler for iOS (you can read more on that here). Unsurprisingly, Windows Store apps based on WinRT have the same limitation.
So I regretfully concluded that I couldn’t use C# for my scripting language, and began to look around at other options. I didn’t see anything that particularly excited me, so I wrote my own programming language. And so today, ladies and gentlemen, I present to you the entirely new scripting language…
“Kablooie”
if (true)
{
img label = img.Label;
label.SetTextWithKnownChars("Hello, world!");
float durationSec = 0.4;
label.SetAnimationAbs(Alpha, 0, 1, durationSec, EaseIn);
// The following line would generate a compile error (type mismatch).
//
// duractionSec = "test";
//
}
// Another compile error (variable not defined/in scope here)
//
// label = img.OtherLabel;
//
Kablooie is a language inspired by C#, but currently rather lazily thrown together on an as-needed basis. So for example, I never bothered to implement the unary increment operator.
for (int i = 0; i < 5; i = i + 1)
{
// i = i + 1 ??! Dude, who writes that anymore?
}
One of the most useful features Kablooie provides is easy closures and deferred execution. And modifying captured variable(s) will affect the calling context, if that context still exists. Here’s the Kablooie built in language keyword delaysec, that executes a block of code after a number of seconds:
img label = img.Label;
float waitSeconds = 4;
delaysec(waitSeconds)
{
label.SetTextWithKnownChars("Done waiting now.");
delaysec(waitSeconds)
{
// Execution of this line is delayed by (2 * waitSeconds).
label.SetTextWithKnownChars("Ha, just kidding, actually waited some more.");
}
}
// Execution continues here without stopping, so this is the first message shown.
label.SetTextWithKnownChars("Please wait...");
I borrowed some features from the Apple/Objective-C world that I liked. For example, easy named notification broadcasting/subscription:
handle("OnScoreChanged")
{
// Note: this string usage generates no heap garbage in the
// long term, thanks to StringBuilder/aggressive pooling behind
// the scenes.
string scoreStr = "";
scoreStr.AppendIntToStr(someScoreIntValue);
img.ScoreLabel.SetTextWithKnownChars(scoreStr);
}
// Send notification to any and all who are listening.
scn.this.SendMotification("OnScoreChanged");
I also patterned my touch events and related default UI element focus capture behavior similarly to the touch events in Apple’s CocoaTouch, as I find that a really natural system to work with:
img.MyFancyButton.Handle("OnTouchUpInside",
{
// User has touched this button, so do something.
// Could also intercept OnTouchDown, OnTouchMove,
// OnTouchMoveOutside, OnTouchMoveBackInside, or OnTouchUpOutside.
});
The Kablooie language contains language primitives to work with the object hierarchy from my app engine (as described in this earlier post). The language keywords "scn" (scene), "chr" (character), "ani" (animation), and "img" (image instance) function both as standalone type names when declaring variables, as well as for namespace roots for access into the actual object instance hierarchy.
// You can find an object by name, searching closest-first by current context...
img someImage = img.NameAssignedToAnImageInstance;
chr someCharacter = chr.NameAssignedToACharacter;
ani someAnimation = ani.NameAssignedToAnAnimation;
// ...or you can access by fully qualified hierarchy path.
img anotherImage = chr.SomeCharacter.SomeAnimation.AnotherImage;
// Array syntax is supported for multiple copies.
img.Button.SetCopyCount(5);
for (int i = 0; i < 5; i = i + 1)
{
img.Button[i].SetIsFlagSet(PhysicsShapeRectangle, true);
}
// Special syntax for using a string variable's runtime value as a name.
string objectName = "Button";
img myButton = img.~objectName~;
Depending on context, there may be a current scene, character, animation, and image. So I allow the "this" keyword under each of the four type hierarchy roots to grab those instances (if available in current context, otherwise a runtime error):
img currentImage = img.this;
scn currentScene = scn.this;
Imperfectly static
All the variables and program syntax are statically checked for both type and syntact correctness. But one thing that’s kind of regrettable to me is that all of this access into the game object namespace (e.g. img.ImageName) is late bound. It would fit better with the ethos of my language goals to verify those names at compile time. The problem is that the engine supports changing an object’s name at runtime, so static checking of object names isn’t really a solvable problem. I guess I could provide some light "linting" functions in my editor, but since laziness happens to fit very well indeed with the ethos of my implementation in general, that isn’t happening anytime soon.
No net garbage
An absolutely necessary feature of this language is that it must not cause garbage collector churn (see earlier post explaining why). This affected a lot of my implementation choices for the language.
For example, when writing my compiler, I used all kinds of the most fun C# features like LINQ and lambdas — the cooler, the better. But for the runtime, I kept everything boring and strict, and avoided allocating memory except through pools. My design environment is a Windows desktop machine with 32 GB of RAM, whereas the runtime has to perform acceptably on some wimpy hardware.
There are other details of the language not covered here, such as the quick and dirty templating/macro scheme, more syntax details, and the system for binding into my engine’s (hundreds of) predefined functions.
Not quite NASA-ready
I am sure that someone with more compiler writing chops would have done a nicer job with the Kablooie implementation, but at this point it’s very usable for me. My compiler is very much quick and dirty — it generates an abstract syntax tree, flattens it, and directly binarily serializes that as the executable code format, with size optimization hacks inserted as many places as I could find to add them — otherwise the code generated would be even more embarrassingly huge than it already is.
All in all, though, it has been one of the most satisfying projects in my career. It is immensely pleasing as a geek to reflect that a respectable fraction of all the lines of code written in my off hours over the last year were in my own programming language.
Oh, and why the name Kablooie? Its counterpart, the designer/editor is called Gooey. I’d suggest that the name also ably communicates the language’s level of dignity and sophistication. 😉