This is an example of an UdoScript program showing some of the currently implemented features. Note that the out statements print to the console and are for debugging - this feature will probably be removed when it is implemented into the game.
// prototypesbool f(int);f(string s,int &i);procedure boo;string name="Udo",time="Now"; // global dataprocedure paul{ int x=10; out x,end; f(name,x); f(x); // function overloading out x,end; boo; // procedure call syntax}procedure boo{ out "boo!",end;}bool f(int i){ if(i==10) out i,end; return true;}f(string s,int &i){ out s,end; i=i*2;}
Currently supported features are:
Function overloading may seem like an odd feature in such a small and limited language, but it is almost a freebie - I have to validate the parameter types anyway and it is quite trivial to then use this to do function selection.
The compiler turns a text file into bytecode based on the following fairly small instruction set:
enum OpCodes { ocEnd,ocCall,ocRet, ocSetRx,ocSetAx,ocMovRA, ocGet,ocPut, ocPush,ocPopN,ocPoke,ocPeek, ocMPush,ocMPop,ocMPSA, ocJmp,ocJz,ocJnz, ocAdd,ocSub,ocMul,ocDiv,ocNeg, ocEq,ocNEq,ocGt,ocGtEq,ocLt,ocLtEq, ocAnd,ocOr, ocOutStr,ocOutInt,ocOutEndl };
A procedure is basically just a function but procedure addresses are stored in a map against their name, so can then be called from the host application by name.
The idea is that you will have like a "startup" procedure, a procedure than runs once a frame, then stuff like doors can have a procedure specified through their "on_enter" property, which can be set in the editor.
And so on and so forth.
This is the current test driver showing how it is used:
#include #include "script.h"int main(){ try { ScriptEngine E; E.OpenScript("sample.txt"); E.Execute("main"); } catch(const ScriptError &E) { std::cerr << E.What() << "\n"; }}
Eventually, script.h will define a pure virtual ScriptListener interface that will be passed to the ScriptEngine in its constructor. ScriptListner will provide a couple of methods that will allow for communication between the script virtual engine and the host during execution.
There is currently a register() keyword that you access in the script like:
register(8)=int("hello");
or whatever. I think that the way things will work is that you will fill out registers, call a service() method in the script that will in turn call a method of the ScriptListener interface, passing in the registers and a reference to the ScriptEngine.
In the host application, you will then derive an application specific from ScriptInterface that implements the Service() method and translates this into actual calls with the parameters translated back into C++.