Hello, first WIP I've made in a while. I had some free time so I decided to revisit the subject again and give it another shot.
I'm inspired by Lua's simplicity and flexibility, but I also like UnrealScript's object-oriented approach to scripting, which I think allows for better expansion. So I took both and mushed them together and this is what I got.
Zen - Scripting language with bindings to C++ and Dark GDK
Scripts are written in external
.zen files, and are linked against the application using C++. I guess the best way to show you is by example:
MyPackage.zen
function main()
{
print "hello, world!";
}
Linking in
main.cpp
Script script("MyPackage");
// Call 'main' method
script.Call("main");
Output
The scripting language features many features, most of which are optional. If you can use DBPro, you can use this. However, if you can use C++ or Lua, there are many features you can exploit. I'll explain the main ones.
Classes:
Classes are essentially the same as classes in C++. They are always allocated on the heap and the garbage collector takes care of managing the memory.
class Human : GameObject // a human 'is a' GameObject
{
var Name = "";
var Age = 0;
function Human() // Constructor calls parent constructor
{
}
function Human(name, age) // another one
{
Name = name;
age = age;
}
function Say(msg)
{
print Name + " says: " + msg;
}
function Run()
{
move(10);
}
function Walk()
{
move(3);
}
}
var me = new Human("Alastair", 20);
me.Say("Welcome to Zen!");
What makes classes powerful is their ability to subclass existing classes. This means that from the minute you begin to write your class, you already have a load of built-in functionalities. Classes support the ability to call base classes' methods, and support polymorphism.
Tables:
Tables are essentially the same as the tables in Lua, though they don't have all the same features such as metatables etc. Generally however, they are exactly the same.
function main()
{
var table = {}; // blank table
table.add(42);
table.foo = {"hello", x = "bar"};
print table; // Result: [42, foo = [hello, x = bar]]
print table[0]; // Result: 42
print table.foo[1]; // Result: [x = bar]
}
Tables can also be used for configurations. Say you had these settings in a
.zen file:
var data = {
players = {
{name = "me", hp = 100},
{name = "you", hp = 0}
}
};
You could then read values as such from C++:
Script script("MyPackage");
Value plrs = script["data"]["players"];
Value x = plrs[0]["name"];
std::cout << x.ToString(); // Result: me
As 'x' is a string, you could even call methods on it, i.e. x.Call("length") (Results 2)
Bindings:
Scripts can be written from inside C++ and added to a global API. The advantage here is that methods aren't run by the virtual machine, but rather they are pointed to methods written natively in C++, which makes them very quick. Core libraries should be written this way. Any APIs written in C++ will automatically be exposed so scripts, so you can use them as if they were right there in the scripts.
Here's an example of how to add a class from C++:
API::GetInstance()->AddAPI( (new Class("MyClass")) // Create class "MyClass"
->AddMember("x") // with field "x"
->AddMember("foo", 3, &Foo) // a native method "foo" which takes 3 arguments and is linked up to callback "Foo"
->AddMember("MyClass", 0, NULL) // .. and an empty constructor
);
We can then use it from all our scripts:
function main()
{
var bleh = new MyClass();
bleh.x = 42;
bleh.foo(1, 2, 3);
}
So that's a brief summary of what's currently working so far. I plan on squeezing out as many bugs as I can find and doing some optimisations here and there, and then work on better GDK bindings. Then I'll be releasing it for free to whoever wants it. I would like to see what people can come up with playing around with it, and help on getting a standard library up would be awesome!
Lemme know whut you think.
"everyone forgets a semi-colon sometimes." - Phaelax