Vassal 4:Design Blog 4
30-Aug-17 Vassal 4 Scripting Language review
After researching the options available, there seem to be three viable choices: Python, Javascript and Lua.
I performed a brief review of the options against our requirements, before choosing one to investigate in detail and build a prototype to test how easy it will be to integrate with a C++ Vassal.
Based on the results of this review, I proceeded to build a prototype of a detailed implementation of Lua as a Vassal scripting language. A detailed design proposal to follow.
Python
I ruled out Python fairly quickly for 2 main reasons.
Primarily, it is due to the sandboxing problems. Python is deeply introspective and nobody seems to have solved the security issues. There has been more than one attempt to build a Sandboxed python that have been abandoned by their developers as unachievable. On top of that, there are zillions of expert Python programmers looking for a challenge to break other people’s software.
Secondly, Python is big. It is a huge system, vastly more than we need.
Javascript
Javascript comes in many varieties.
The big engines with JIT compilers like V8 and SpiderMonkey I discounted. They have huge footprints and portability issues. The source is available, but would be difficult to maintain or modify by us, we would be dependent on their support groups for bugs or issues we found.
I looked for lightweight engines and found 2 that would work for us, MuJS and Duktape.
Both are lightweight Javascript bytecode VM engines designed for embedding with C source, Duktape seems reasonable popular. Their big problem is speed. In a simplistic raw script speed benchmark, I found MuJS to be 6 times slower that Lua and Duktape to be 10 times slower. (Out of interest, Beanshell is 30 times slower than Lua!!).
Interestingly, the design of both MuJS and Duktape where heavily influenced by the internal design of Lua.
Javascript also has quirks that will bite us, such as the lack of a separate string concatenation operator (5 - "1" = "4", 5 + "1" = "51").
Javascript also has problems with introspection that make sandboxing difficult.
Lua
Lua seems to tick all the boxes.
Lua is a small, consistent language that is implemented in a lean and fast bytecode VM interpreter. While being a dynamically typed language, internally, it is typed, so string are stored as string, integers and ints and floating point numbers a float’s. Lua has a single data-type, the table, which is essentially an associative array, but includes a unique ‘metatable’ concept that makes the table a very flexible and powerful construct. It allows, among other things, for Global Variable usage to be disabled.
Lua has a separate string concatenation operator which resolves problems like does 20 + "08" equal 28 or "2008".
The source code of Lua is ANSI standard C and can be compiled directly into Vassal which is the recommended way t use it when embedding. It is easy to call from C++ and for Lua to call out C++.
The source is easy to understand and should not difficult to modify. Modifcation of Lua to suit your application is encouraged.
Lua has been designed to be sandboxed. It is easy to cut-down the environment a Lua script runs in to minimise access to unwanted built-ins and language features.
Lua has only one form of introspection, which resides in a module which is easily excluded from a scripts environment.
Lua includes hooks to support a debugger that can be used to implement CPU and Memory limit checking.
Lua has an internal error handling mechanism that can be used to create a consistent error handling mechanism.
Lua is able to compile a script to bytecode and report errors without running the code, and to cache and re-use the compiled bytecode so that repeat execution of the same script does not require a rec-compile of the script source, even if the same script is being run by different Vassal Game Objects.