Please Sign up or sign in to vote.

The source code is already available in github.

Other Links

Introduction

ZetScript is a programming language with an API that allows to bind C++ code in script side. ZetScript includes the following features:

Virtual Machine

Language syntax close to JavaScript

MSVC++ 32/64 bits MSVC 2015/2017 or build tools v141

Linux/MinGW 32/64 bits, g++ 4.8 or above

Save/Load state support

Dynamic Garbage collector

Straightforward way to bind C++ variables, functions, classes and its members

The library size is 1Mb on gnu toolchain and 372KB on MSVC++

Override operators through metamethods

A helloworld in shown in the following code:

#include " CZetScript.h" using namespace zetscript; void say_helloworld(){ printf( " Hello World!" ); } int main(){ CZetScript *zs = CZetScript::getInstance(); register_C_Function( " say_helloworld" ,say_helloworld); zs->eval( " say_helloworld();" ); return 0 ; }

The code presented on list 1.1 it registers C function say_helloworld with just one line of code. Then, it calls eval function that evaluates the string say_helloworld(); that means execute a call of C function say_helloword from the script engine.

Install

From the source code included in this article, the install is quite easy, it uses cmake method so to configure the project depending which compiler is installed:

cmake CMakeLists.txt

Quote: Note: MSVC 2017, it has a feature a Open Folder that easily configures a CMakeFile project just opening the folder where the project is, avoiding configure the project in the same directory and leave a lot of files and directories related with configuration

Yet Another Script Language ?

Few years ago, I decided to start work with scripting because I needed to be faster in terms of production. Of course, because I always try to be a good engineer, I started to play with all script engines that already are in this world.

Some of them were faster but it I didn't like its syntax. Others, the syntaxes were good but slow at run-time, etc. After spent a lot of time playing with them and trying to decide what kind of script engine could be a better candidate for my project, I finished to know that none of them fitted at all for my needs.

So two years ago, I decided to start my own script engine, but apart of putting my effort with my needs, I read from some forums what it expects to have from a good script engine. I mixed both things together and it became ZetScript.

At the end, I decided to make ZetScript free because I like to make easy life for people, just in case they find this tool useful.

Language Overview

Built-in Types

Zetscript has built in basic types like integers, numbers, booleans and containers as vectors and structures.

var i= 10 ; var f= 0 . 5 ; var s= " a string" ; var b= true ; var vector=[ 1 , 0 . 5 , " a string" , true ]; var struct={ i: 10 , f: 0 . 5 , s: " a string" , b: true , v: [ 1 , 0 . 5 , " a string" , true ] };

Scope

ZetScript has an easy concept of global scope declaring variables at the top of evaluating scope. Local variables are within blocks like function or loops. Local var s are destroyed when it exits from block, unless it is not referenced by other variable.

var i= 0 ; { var j= 2 ; }

Conditionals

Zetscript supports if - else and switch conditionals:

var number= 5 ; if (number < 10 ){ print( " number less than 10" ); } else { print( " number greater equal than 10" ); } switch (number){ case 0 : case 1 : print( " number is 0 or 1" ); break ; case 2 : print( " number is 2" ); break ; default : print( " number is : " +number); break ; }

Loops

Zetscript supports while , do - while and for as loops iterators:

var i= 0 ; while (i < 10 ){ print( " i:" +i); i++; } do { print( " i:" +i); i++; } while (i < 20 ); for ( var j= 0 ; j < 10 ; j++){ print( " j:" +i); }

Classes and Inheritance

Zetscript supports class and inheritance. Member and function variables inside class scope are referenced through this keyword. Also, it can include variables/functions in post class declaration through operator :: . Inheritance supports super() function in order to call parent function. To instance class is done through new operator.

class Test{ var data1; function function1(a){ this .data1 =a; print( " calling from Test. Data1:" +this.data1); } }; var Test::data2; function Test::function2(){ this .data2= " a string" ; } class TestExtended: Test{ var data3; function function1(a){ super ( 2 ); this .data1+= 5 ; print( " calling from TestExtended. Data1:" +this.data1); } function function3(){ this .data3= 6 ; print( " data3 is " +this.data3); } }; var t= new TestExtended();

Calling Script Function from C++

Once you have evaluated the script, you can call function script from C++ until up 6 parameters. To call script function from C++, it can be done through the function CZetScript::bind_function . Just pass the name of the function with the C++ template casting and it creates an std::function that links the script function. Also, it is possible to link/call function member from instanced object on global scope.

#include " CZetScript.h" using namespace zetscript; int main(){ CZetScript *zs = CZetScript::getInstance(); zs->eval( " class Test{" " var data1;" " function function1(arg){" " print(\"calling Test.Function:\"+arg);" " }" " };" " " " function delete_test(){" " delete test;" " print(\"test variable was deleted\");" " }" " " " var test=new Test();" ); std::function<void()> * delete_test=bind_function<void ()>( " delete_test" ); std::function<void( int )> * test_function1=bind_function<void ( int )>( " test.function1" ); (*test_function1)( 10 ); (*delete_test)(); delete test_function1; delete delete_test; }

API Overview

Bind C++ Variables

Zetscript can bind basic C types as int , float , bool and string types to operate in the script side.

#include " CZetScript.h" using namespace zetscript; int main(){ int i= 10 ; string string_var = " in c++" ; bool b= false ; float f= 5 . 0 ; CZetScript *zs = CZetScript::getInstance(); register_C_Variable( " i" ,i); register_C_Variable( " b" ,b); register_C_Variable( " f" ,f); register_C_Variable( " string_var" ,string_var); zs->eval( " i+=10;" " b=!b;" " f+=10.5;" " string_var+=\" and in script\";" " print(\"string_var:\"+string_var);" ); return 0 ; }

Bind C++ Classes and its Members

Binding C++ class in Zetscript is done easily with register_C_Class method. To bind variables and functions members, it can be done through register_C_VariableMember and register_C_FunctionMember respectively. In script, you can instance the C++ class in script side through operator new . When the instanced C Class variable is not used anymore, the user has to delete it with operator delete .

#include " CZetScript.h" using namespace zetscript; class MyClass{ public : int data1; void function1( int arg){ this ->data1 = arg; printf( " Int argument is %i

" , this ->data1); } }; class MyClassExtend: public MyClass{ public : float data2; void function2( float * arg){ this ->data2 = *arg; printf( " Float argument is %.02f

" , this ->data2); } }; int main(){ CZetScript *zs = CZetScript::getInstance(); register_C_Class<MyClass>( " MyClass" ); register_C_Class<MyClassExtend>( " MyClassExtend" ); class_C_baseof<MyClassExtend,MyClass>(); register_C_VariableMember<MyClassExtend>( " data1" ,&MyClass::data1); register_C_FunctionMember<MyClassExtend>( " function1" ,&MyClass::function1); register_C_VariableMember<MyClassExtend>( " data2" ,&MyClassExtend::data2); register_C_FunctionMember<MyClassExtend>( " function2" ,&MyClassExtend::function2); zs->eval( " var myclass = new MyClassExtend();" " myclass.function1(12);" " myclass.function2(0.5);" " print(\"data1:\"+myclass.data1);" " print(\"data2:\"+myclass.data2);" " delete myclass;" ); return 0 ; }

Inheritance C++ Class on Script Class

An important feature of ZetScript is that it has supports C++ class inheritance for script class. this and super() keywords work as a normal behavior.

From list 1.2, we present an example script that ScriptMyClassExtends class is inherited by MyClassExtends class (from C++):

class ScriptMyClassExtended: MyClassExtend{ function function1(arg1){ print( " script argument is " +arg1) super ( this .data1+arg1); } } var myclass= new ScriptMyClassExtend(); Myclass.function1( 5 );

The code shown at list 1.3 will print:

script argument is 5 c++ argument is 15

Metamethods

ZetScript implements metamethods to map operators or other operations through objects. Currently, it supports the following metamethods:

_equ (aka ==)

_not_equ (aka !=)

_lt (aka <)

_lte (aka <=)

_gt (aka >)

_gte (aka >=)

_not (aka !)

_neg (aka -)

_add (aka +)

_div (aka /)

_mul (aka *)

_mod (aka %)

_and (aka &)

_or (aka |)

_xor (aka ^)

_shl (aka <<)

_shr (aka >>)

_set (aka =)

For example, if in script side we want to do the operation + for an object, we have to declare the function _add with two parameters, as we can see in the following code:

class MyNumber{ var num; function MyNumber(_n){ this .num=_n; } function _add(op1,op2){ return new MyNumber(op1.num+op2.num); } }; var n1 = new MyNumber ( 20 ); var n2 = new MyNumber ( 10 ); var n3 =n1+n2; print( " n1 (" +n1.num+ " ) n2 (" +n2.num+ " ) = " +n3.num);

The same can be applied for C++ class. We have to register _add function in the C++ object:

#include " CZetScript.h" using namespace zetscript; class MyNumber{ public : int num; MyNumber(){ this ->num= 0 ; } MyNumber( int _n){ this ->num=_n; } void set( int _n){ this ->num=_n; } static MyNumber * _add(MyNumber *op1, MyNumber *op2){ return new MyNumber(op1->num + op2->num); } }; int main(){ CZetScript *zs = CZetScript::getInstance(); register_C_Class<MyNumber>( " MyNumber" ); register_C_VariableMember<MyNumber>( " num" ,&MyNumber::num); register_C_FunctionMember<MyNumber>( " MyNumber" ,&MyNumber:: set); register_C_StaticFunctionMember<MyNumber>( " _add" ,&MyNumber::_add); if (!zs->eval( " var n1 = new MyNumber (20);

" " var n2 = new MyNumber (10);

" " var n3 =n1+n2;

" " print(\"n1 (\"+n1.num+\") + n2 (\"+n2.num+\") = \"+n3.num);

" )){ fprintf(stderr,ZS_GET_ERROR_MSG()); } return 0 ; }

Quote: Note that the codes shown at list 1.4 and 1.5 the _add function is static due operates from two objects and it doesn't the object member itself.

Save/Restore State

Sometimes, it is useful to reset the script cleaning global variables or restore from one point when, for example, C++ application is restarted.

ZetScript supports a way to save current compiled state. This operation saves AST nodes, registered C functions, variables and classes.

To save current state, we have to invoke CState::saveState . This function returns an index that tells compiled state index saved.

int idx=CState::saveState()

To restore a previous state, we have to invoke CState::restoreState passing compiled state index.

CState::setState(idx)

Performance

I had checked ZetScript performance 1.3.0 version with other script languages. Because Lua is the fastest and one of the most scripting language used, I chose this to compare Fibonacci calculation time with N=34.

A Fibonacci script implemented in ZetScript is:

function fibR(n) { if (n < 2 ) { return n; } return fibR(n-2)+fibR(n-1); } print( " fib: " + (fibR( 34 )) );

List 1.6

and the equivalent in Lua is:

function fibR(n) if (n < 2 ) then return n end return (fibR(n-2) + fibR(n-1)) end print( " fib: " .. fibR( 34 ))

List 1.7

And I have compared them through the time command already available in linux and the tests were made on a computer with i5-2450 CPU with 2.50GHz and 8GB of RAM.

The result was the following:

Lua: 1355ms

ZetScript: 3110ms

So I can say that Lua is 3110/1355=2,29 times faster than ZetScript in this test.

Conclusions

I have presented a new programming language, but it is not quite new because it is close to JavaScript so many people can find confortable using it. Furthermore, Zetscript API exposes C++ code in script side in a straighforward way so is quite productive in general.

The language is new and of course, even though it has had several tests, more bugs can be found. I would be happy if people start to use it and gives it feedback.

Maybe it will have success or not. I did this project for my needs but I made it available for free in case other people can find it useful for their projects.

Changes since 1.3

Implements an interactive console

Added add/remove attributes on struct variable

variable Optimized eval process

process Improve virtual machine speed ~x2

Minor bug fixes

Changes since 1.2

eval process can be split in parse/compile and execute (see seccion 2.4 from ZetScript documentation)

process can be split in parse/compile and execute (see seccion 2.4 from ZetScript documentation) As virtual classes change its memory map at run-time, function and variables cannot ensure the same pointer as base class so it has been decided to disable heritate all functions/variables from parent classes (only C). Due that change, now we have to pass class type on both register_C_FunctionMember and register_C_VariableMember .

and . ZetScript 1.1.3 supported automatic register of parent functions/variables but, due to the problem of virtual functions, it cannot do since 1.2. Derived classes have to re-register parent functions/variables in order to use in script side.

ZetScript 1.1.3 allowed to pass float type as arguments but this only works 100% for x32 builds. So to avoid confusion, I decided to constraint pass float types as pointer (i.e., float * ).

History