viksoe.dk

BaseScript


This article was submitted .


While considering how to best integrate a scripting language in a new product, I realized that I couldn't use Microsoft JScript as I usual do. Integrating with scripting means that you allow users to write and execute scripts that automate the behaviour of your application. Most products can benefit from integrating scripting into the application, because it allows users to extend your tool with their own ideas and enables a test team to write automated test scripts.

Adding a Microsoft ActiveScript scripting language, such as JScript, is quite simple (see my old WTL sample). However, recently the number of support calls from concerned customers worried about their antivirus software complaining about "malicious script" alerts have grown. Antivirus products seem to now trigger simply because an ActiveScript is being used. So while I consider which antivirus vendor to flame for generating needless support calls and wasting my time by disabling a perfectly good technology in my application - I went on to write my own scripting language replacement.

Using BaseScript

The BaseScript is a scripting language with C or Javascript-like syntax. The language supports the usual language constructs, such as if, for, while and switch statements. It has support for all the base intrinsic data-types, such as integer, string and decimal. All variables are declared typeless and at present arrays and structures are not supported. You can declare new functions, but structures and expando objects are not available.
function Foo(a) {
  return 3 + a;
}

var x = 0;
for( var i = 0; i < 20; i++ ) {
  x = Foo(i);
  if( x == 15 ) break;
}

It is a fully interpreted language, which means that unlike most languages it does not pre-parse the code and executes pseudo-code, but instead repeatedly parses over the text and interprets its meaning. This makes the parser code very simple (it's just 600 lines of code). Obviously the penalty is a somewhat slower execution time. I'm not concerned about that at the moment, because the scripts I'm going to run are not huge and don't have to be run in a performance critical tool.
The entire scripting engine can be compiled down to a DLL of measly 45 Kb.

The crux of the language is of couse the tight integration with a C++ application.
To create a script engine and execute some code, the application does the following:

 CBaseScript* pScript = CreateScriptEngine();
 pScript->RunScript("var i = 5;");
 DestroyScriptEngine(pScript);
To extend the language syntax with global variables and objects, the application registers a callback function:
  pScript->RegisterGlobals(&globals);
  pScript->RegisterObject("App", &app);
Each of the C++ callback objects must implement a simple interface IScriptCallback:
class IScriptCallback
{
  bool GetProperty(LPCWSTR pstrName, VARIANT* pvRet);
  bool SetProperty(LPCWSTR pstrName, VARIANT vValue);
  bool Method(LPCWSTR pstrName, VARIANT* pvArgs, int nArgs, VARIANT* pvRet);
};
The scripting engine calls these callback functions to allow the application to define custom functions and variables. With the RegisterGlobals method the application can register new global functions and variables. The RegisterObject allows the application to register an object, which can be accessed in the script. Like this...
Application:
pScript->RegisterObject("App", &myapp);

Script:
var i = App.getVersion();
All variables used in the script must be declared with the var syntax. When the engine stumbles across an unknown variable name it turns to the callbacks and asks them if they know the variable or function. A callback returns true if it does and returns the value in the VARIANT pvRet output argument.

The engine exposes a number of runtime functions:

 len = strlen(str);
 str = strstr(str);
 pos = strpos(str);
 str = strupper(str);
 str = strlower(str);
 i = atoi(str);
 str = substr(str, pos[, len]);
 f = fopen(str, "rwa");
 fclose(f);
 str = fgets(f);
 b = fputs(f, str);
 b = feof(f);
 sleep(i);
 eval(str);
 exit();
These mostly deal with strings and files. Obviously it is possible to add as many more you'd like.

It's a very simple scripting language, but gets you going very quickly. Even if the language syntax is very Javascript-like it doesn't mean that it actually conforms to the ECMA standard or any sorts. The language does have a few quirks where it may allow strange constructs or not quite deliver useful error messages. But it's so easy to integrate into your application that it will definitely be a bonus.

Getting serious

Obviously this is merely a little scripting language hacked together for a specific problem I had. If you really want to add get serious with integrating a scripting language into your application, you really need to take a look of some of the stable free products out there, such as Duktape Javascript, LUA or Python. While C++ integration may not be as straight-forward, you can find help over at the SWIG project.

Source Code Dependencies

Microsoft Visual C++ 6.0

Download Files

DownloadSource Code (19 Kb)

To the top