I thought I would do a feature list for voodoo. This is to keep my head straight about what I’m doing, and also act as the reason I’m making voodoo, because no other language has the features I want.
Simple Syntax
The general constructs should be obvious, and there will be no obscure symbols.
Static Typed
This means that variables always have a type and the compiler does the type checking. This is good because if a program compiles, it has a higher chance of working at runtime. This also helps with programming because small mistakes will be picked up at the compilation stage. Also the typing information can be used in an editor and other programming aids.
Tabbed Block
No more braces and only one type of if statement. This means that the code after a statement that takes a block of code, such as a for loop, just needs to be indented. In other languages it’s a requirement to have curly braces around the code block, and indentation is optional. I think tab-blocked languages look nicer and are easier to read.
File independant
Any file can add prototype definitions to any editable class. The same file that defines an interface can define which classes implement the interface and declare the methods and implementations needed for the implementation. This way separate functionality can be grouped together independently of object. This is great for source control because multiple people can work on one object at once. Whole functionality can be included or excluded just by including or excluding a single file.
Exception Handling
Simplified exception handling, you can either have a handler for the entire method, or have a block of code that handles exceptions for a certain method call. I’ve found that these are the most common (if not only) cases where exceptions are used. Try blocks are messyâ„¢.
Pure Object Oriented
This means that all type and data structures are objects. This simplifies the virtual machine but complicates other aspects. All objects inherit from the class named “Type”. All objects that can be subclassed inherit from the class “Object” which itself inherits from “Type”. Methods and classes are also objects and have methods and properties themselves. Classes behave in a similar way to Objective-C classes where they exist as a standalone instance with separate methods to instances of the class. Each class actually has two definitions, one for the class ‘owner’ object, and another for the actual instances of the class.
Single Inheritance
Multiple inheritance is too complicated and unnecessary for most situations. By having a inheritance tree dependancies are simple and method overlap makes sense.
Interfaces
To allow a type of multiple inheritance, interfaces are used and will function the same as Java Interfaces. They can be thought of as a ‘protocol’ that every class that implements the interface must adhere to. An interface will define methods that the classes that implement the interface must also have with the same name and equal or broader variable types. This means that if something is more ‘generic’ it’s okay.
Code Blocks
A method can be called with an attached code block, that resides in the calling method. While the method is executing it can call this block of code any number of times with different arguments. This will be how for loops, if loops and other block structures will be implemented. The code is fixed in place, and so can reference variables from outside it’s scope.
Native Code
To extend and develop functionality for voodoo, C++ is used. A single C++ class is responsible for native methods introduced at that class level. So an integer will have three classes, one will be the C++ native class responsible for the implementation of the “Type” class, one for the native implementation of the “Object” class and another for the implementation of the “Integer” class. Static methods are called when the class is loaded and unloaded. Member methods of the C++ object are called for initialisation (init) and when the object is going to be deleted. A method is either implemented by voodoo code, or a single method in the C++ object that handles that voodoo class.
States
A class can have many sub-classes called ’states’. States can’t add any new properties, constants, implemented interfaces or methods, but they can have alternate implementations of existing methods. The special thing about states is that because they are the same size and have the same offsets as their sibling states, an object can have it’s class changed at runtime. This is great for game development because they tend to have state like actions and events within the game world. A simple example would be an elevator, it could have 3 states: going down, going up and stopped. Three classes would be interchanged for any given elevator object as it’s used in-game. Each game ‘tick’ would call a method, the going up elevator would add something to it’s height value incrementally until it reaches a certain point where it would change itself into the stopped state. Each tick the stopped state would do nothing but check if the button is pressed and if so it would change it’s self into whatever state is appropriate. The going down would be similar to the going up state. The great thing about this implementation is that the code is so simple. An implementation in a language without states would either require a enum, or some sort of wrapper class.
In the actual implementation there would be an event system, so only objects that need to be called every tick are notified. The button would call the method and the elevator would register itself with the event system so it’s called every tick.
Data - Code crossover
XML data for other information including 3d information, level data and other game content can refer to objects and methods of voodoo code. Objects of a certain class can be linked to a game asset and instantiated whenever an instance of that asset is spawned in the game world. Method delegates are called for various events concerning the object.
Search paths and soft class referencing
Multiple search paths can be setup for both the compilation stage, and the running stage. References to classes go through a cascade of search locations for their definition.
Network Proxy Objects
Objects can be created as a network proxy for another object residing on another computer. These are transparent to other classes and programming. If fine control of the interaction is needed (for optimising) the same system of control that is used in a threaded environment is used in a network environment. Execution on another machine is modeled as another thread so method calls can be synchronous or asynchronous.
Threads
Threads are implemented by swapping between bits of script rather than creating OS based threads which means that there can be more executing at once without swamping resources. Because all the fundamental instructions are in C++ and have an indeterminate execution time C++ methods should be short. Execution will either be time based, or number of instructions (profiling needed). Methods can be called with a ‘async’ (asynchronous) modifier which means that the method will spawn a separate thread, and execution in the calling method will continue without waiting for it to return. The modifier syntax is object.method.async(param1, param2) compared to the normal call syntax which is object.method(param1, param2). Methods called asynchronously must return void (nothing).
C++ calling voodoo methods
The idea with C++ calling voodoo is that each method (or groups of methods - it doesn’t matter) has a voodoo method object. Each voodoo method object has a constant pool and a link pool, these are objects and constants that the virtual machine keeps track of and relinks when necessary. So the C++ calling method would access these objects in a similar way to voodoo bytecode, by offsets (or a hashmap or whatever). So when a C++ method wants to use a voodoo integer it just uses the one in the constant pool that it registered earlier. This way everything is linked together still and it will be faster than a lookup every method call.
There are two ways for C++ to call voodoo methods. One is the asynchronous method where the calling function waits in another thread until the voodoo thread that it created finishes. The other way is where the virtual machine runs a voodoo thread singularly until it has completed and returns back to the calling C++ code. The second option can only be used by the same thread that’s executing voodoo (unless I make voodoo thread safe) and should only be used for small calls or in a single voodoo thread environment.
I’ll add to this later…