Click here to Skip to main content
13,835,377 members
Click here to Skip to main content
Add your own
alternative version


Posted 18 Dec 2018
Licenced CPOL

VRCalc++ Object Oriented Scripting Language :: Vincent Radio {Adrix.NT}

, 27 Dec 2018
Rate this:
Please Sign up or sign in to vote.
VRCalc++ Engine is embeddable in any Delphi Application using Dynamic Packages


Born as a simple calculator VRCalc++ featurs the same operators as C++ and Java
and more extra objects related operators implemented using Delphi object interfaces
as well as a complete set of procedural flow control structures including
- selection statements,
- loops control statements,
- labelled statements,
- exceptions control statements
and so on ...
VRCalc++ includes support for
- access the RTL package data, funcs and procs
- run-time exceptions handling
- multi threaded functions and objects
- Windows DLL access (Win32 version)
and more ...
VRCalc++ is embeddable in any application using Delphi packages
because its scripting run-time code and other environment support
exported functions are also contained in Delphi packages
VRCalc++ is extensible
by object oriented scripted class modules
or by external packages
implementing and exporting the required functions and interfaces

VRCalc++ Scripted Class Modules usually define and export functions as well as properties

the VRCalc++ base independant script Engine code
can also be recompiled and run under Linux and MacOS
VRCalc++ Console is a Windows Application
using the VRCalc++ Scripting Engine Package.
it can also be used as a simple, immediate and fast calculator
by executing simple scripts or scratch code

VRCalcSX is a Console Program that only executes VRCalc++ scripts

both of them are extensible by pluggable modules

VRCalc++ Plug in Modules are *.BPL Files (Delphi Packages) responsible for registering
the required external functions into some namespace in the Global Root
which is accessible using the At Operator (@)
so they can be addressed by scripted code

VRCalc++ Links

you can find
VRCalc++ Source Code, Delphi Build Projects,
VRCalc++ Scripts Samples, VRCalc++ Documentation
and more Delphi Projects by Vincent Radio {Adrix.NT} on at


I inspired myself to the FRED Programming Language of Framework III by Bob Carr (Forefront Corporation)
(first developed in 1983 and distributed by Ashton-Tate) in developing VRCalc++ OOSL.

Language Overview

in VRCalc++ everything is evaluated as an expression that returns a value
or raises an exception,
even when you declare a function or a property.
however the value is discarded unless you assign it to a variable using the "=" operator.
expressions are separed by the comma symbol (,)
and can be enclosed in normal (...) or curly {...} blocks.
there are 3 types of code blocks
(...) : normal used in expressions and function body blocks
[...] : square used for indexing and names
{...} : curly used in expressions and function body blocks
integer number literal constants can be specified using a sequence of the digits (0 .. 9).
you may also specify the type of the integral (its size in bits) along its value (default is the biggest int type).
you may also specify if it is a signed or unsigned integer.
support for hex constants is also provided.
floating point number literal constants can be specified using a sequence of the digits (0 .. 9)
followed by a dot (.) and a sequence of digits.
after you may also specify the exponent using (E | e) an optional sign (+|-) and an int number.
after you may also specify the (F | f) to indicate that it is a floating point number.
string literal constants can be specified by any sequence of chars enclosed by double quotes "..."
like this: "this is a string constant"
string literals can be split by spaces like this: "hello " "adrix" " you are an awesome programmer"
the result is always concatenated
char literal constants can be spefified using single quotes to surround them  like this '#'.
both string & char constants include support for escape sequences.
the escape char is (\)
for example ...
'\'' // the ' char
'\t' // the TAB char
'\r' // CR
'\n' // LF
'\x' // prefix to specify hex digits
'\d' // prefix to specify decimal digits
note: you may also concatenate strings and other values using the (+) operator
like this:
"hello " + "adrix" + '.' + 5
in VRCalc++ values are Delphi Reference Counted Objects.
in VRCalc++ variables are Objects that implement the ValueHolder Delphi interface.
References to Value Holders are also supported.
all computation involving integral and floating point objects
are performed at the max precision, that is
- 64 bits for integers
- Extended for floating point
after you may convert the result using the provided functions
a VRCalc++ Variable can contain any type of object from time to time.
the type of a Var is the type of value it contains in that moment.
a VRCalc++ Variable is created when it is first assigned in its default context.
however you can explicitly declare a variable in a specified context.
you cannot explicitly declare a Var twice (it raises an exception).
the main contexts for a variable are
- global
- thread (global)
- class (scripted module)
- instance (scripted module instance)
- local (inside a function or a local env)
when you write something like "5 + 2" in a script
then an int object is created for the number "5"
another int object is created for the number "2"
and the result is an integer number object holding the value "7".
note that if you write
a = 5,

b = a
in a script then a and b refer to the same object value
to copy only its value and create a new number object
you have to write something like ...
a = 5,

b = a + 0 // this forces the creation of a new int object
so be aware when dealing with ref counted objects whose semantic is copy !!
VRCalc++ Scripted Classes are stored in Text Files.
a Scripted Class should have a name and should register itself into the
Global Named Values List (starting from the Root).
VRCalc++ Scripted Classes usually defines
- functions
- properties
these objects stored into the Scripted Class Module can be
- static
- instance
static functions and properties can be invoked on the Scripted Class
while instance ones need an instance of the given class
a function can also be private and with an empty name
private functions are not registered into the scripted class module
but they can be passed as arguments to other functions
or their function definition value can be stored into variables
for later invocation.
no lambda functions are present in this version of the engine.
use small scripted classes instances with instance data instead
implementing the given interface.
a scripted function takes a variable number of arguments.
arguments may have a name in a funcrtion definition.
named arguments became local variables on the stack when the function
is later invoked.
if the function call has fewer args then an exception is raised.
there is NO Explicit public interface declaration in VRCalc++
a scripted module defines its interface implicitly by defining
- functions (and arguments)
- properties
the name of a function or a property can be any string.
if this name contains spaces it should be defined as follows: ["this name contains spaces"].
also formal arguments names can contain spaces in the same format.
moreover a name can be an expression enclosed in square braces as follows: [ expr ].
the given expression should return a string object.
VRCalc++ Scripts can contain comments
- multiple line comments start with /* and end with */ symbols (no nested comments)
- single line comments start with the // symbol and extends to the end of line
all comments are skipped by the VRCalc++ interpreter.
Every Scripted Class Module should register itself into the Global Named Values List
starting from the Root in some given Name Space otherwise it cannot be accessible
by user code.
this Global List is a Hierarchical List of name spaces made of Named Objects
that is Objects with a Name implementing the given interface.
a Scripted Module is also a Named Object.
a Name Space is a List that is also a Named Object.
VRCalc++ Plug in Modules have to register functions into the Global Named Values List
into the Root or in some Name Space starting from the Root.
in VRCalc++ the dot operator (.) is used to access
- Name Space members
- Members of a Class
- Members of an Instance of a Class
in VRCalc++ the at operator (@) is used to access the Global Root of registered names (see above).
the VRCalc++ Engine defines some binary and unary operators
the most important of these is the Assignment Operator (=)
other binary operators are ...
is : compares a value against a given class
/ : divide
* : mult
\ : int div
% : modulus
+ : addition
- : subtraction
& : bit and
| : bit or
^ : bit xor
<<, >> : shift left, shift right (overriden and also used for insertions & extractions)
&& : bool and
|| : bool or
some unary operators are
-, + : negation, same value
! : bool negation
& : get a ref to a value holder
* : get a value holder from a ref
++, -- : auto incr, auto decr prefix operators (post fix are NOT supported)
note: some binary operators can also be overriden by scripted objects
some op-assignment operators are supported such as
+=, -= : add/sub & assign
*=, /=, \=, %= : mult/div & assign
and so on ...
the VRCalc++ Engine defines and includes some basic no args system functions (called Script Parsers)
some of them are ...
flow control
if : used to choose among 2 expr values based on the truth value of another expr
switch : used to choose among many values in an exclusive way
while : executes a block while a given expr is true
for : iterates over a container
return : to return a value from a function
label : to befine a labelled block in the script
cycle | continue  : to repeat from the start of a given label or a loop
leave | break : to break from a labelled block or a loop
exceptions handling
try : to handle exceptions (includes a finally block)
raise : to raise an exception
catch : to block the propagation of an handled exception
some usefull constants
true : the true bool value
false : the false bool value
null : the null value
nil : same as null
these built in system function can be addressed by the ($) unary prefix
however it is best to use the exported ones in the Global Named Values List using the (@) prefix
(*) see examples below

VRCalc++ Objects Names

the name of a VRCalc++ named object can be specified in a script using
- an unquoted string starting with a letter (A..Z, a..z) | the underscore char (_)
  followed by any sequence of letters, numbers (0..9) | the underscore char
- a simple quoted string if the name contains other special chars or spaces (example: "this string contains @")
- an expression enclosed in square brackets [...] returning a string value (the expr is immediately evaluated)
Functions, Properties, Vars, Namespaces and so on are all named objects

VRCalc++ Function Definition

to define a Scripted VRCalc++ Function use the following syntax ...
@function FuncName (FormalArgsList) // the formal args list can be empty

    StaticOrInstanceModifier // optional, default is instance

    RegOption // optional, default is public and registered into this class namespace

    DescrOption // optional, default is an empty description string

    Prologue // optional, can be "do" | ":"

FunctionBody // mandatory, can be a "{...}" code block | a "(...) code block
FuncName description:
in addition to the names rules specified above, a function name can evaluate to an empty string
or it can be (@noname | @pure) which evaluates to an empty string, this can be useful if the function
name does not matter because it is a private function called by address.
FormalArgsList description:
a comma separed list of formal args names (it can be empty).
args names follow the same rules for named objects (* see above).
StaticOrInstanceModifier description:
it can be ...
- static | class : to define a class static function
- instance | <empty> : to define an instance function
by default it is an instance function
RegOption description:
it can be ...
- public | <empty> : to define a public registered function, this is the default
- private : to define a non registered function (in this case store its def value into a var for later invokation)
by default it is a public function
DescrOption description: it can be a string constant or an expression enclosed in square brackets
that returns a string value prefixed by the ("descr" | "description") keyword
FuncBody description: it is the body of the function which is a list of comma separed expressions.
the function body can be enclosed by curly braces {...} or by simple braces (...)
the returned value is the last one evaluated by the function
unless the @return <value> system function is called inside the function body.

VRCalc++ Property Definition

to define a Scripted VRCalc++ Property use the following syntax ...
// sample property definition

@property PropName

    StaticOrInstanceModifier // optional, default is instance

    PrivOrPubMod // optional // optional, default is public

    DescrOpt // optional

access (AccessFunctionRef) |

get (GetterFuncRef) set (SetterFuncRef)

PropName : the property name follows the same rules stated for VRCalc++ objects names

StaticOrInstanceModifier : it can be
- class, static : defines a scripted class static property
- instance : defines an instance property (this is the default)
PrivOrPubMod : it can be
- private, internal : the property is not registered and its only accessible by its ref value
- public : the property is registered into the class and it is publically accessible (this is the default)

DescrOpt : the property descr string (same as for funcs)

access (AccessFuncRef) : designates a single function to both get & set the value
the function is called with no args to get the value
the function is called with a single arg to set the value
get (GetFuncRef) set (SetFuncRef) : designates a getter & setter functions pair
the getter function takes no args
the setter function takes a value arg
note: a property object is a ValueHolder so its ref can be taken using the (&) unary operaqtor
and its ref can be dereferenced using the (*) unary operator

VRCalc++ Scripted Class Registration Example

suppose having a scripted class that implements a scripted Random Object
stored into a file named "AxRandomClass.vrcm"
(*.vrcm is the standard VRCalc++ extension for class modules).
the given class module name itself as "RandomObject".
then it creates a name space into the Root named "AxMath"
(the name space is created unless it already exists).
if that class name is already registered it stops script execution
otherwise it continues with functions and properties declaration
and at end it register itself into "@root.AxMath".
here is the code RandomObject,

// create required namespaces if not already
@engine.nvlist.pathfolders.forcecreate {

@if (
    @classmodule.IsNameRegisteredInto ( @root.AxMath )
) then {
    // class already registered: break module load

// continue with functions and properties definitions ...
// ... ... ...
// ... ... ...

// at end

// register this class module in its namespace
@classmodule.RegisterInto ( @root.AxMath ),

Defining a Function Example

let's show some function definitions using the RandomObject example
the following is our RandomObject constructor,
its function name is "Initialize" and takes no args
it creates an instance var named "theRandSeed" and set it to "0"
then it returns @this that is the same object instance
(note that the word @return @this is not needed because a function always returns
the last computed value, in this case @this)
@function Initialize()
    @selfvar.theRandSeed = 0,

note that @selfvar is used to define an instace var.

the following is the definition of the RandSeed property

// random seed property

@to GetRandSeed() { @return @selfvar.theRandSeed },
@to SetRandSeed (aValue) { @selfvar.theRandSeed = aValue },
@property RandSeed get (@thisclass.GetRandSeed) set (@thisclass.SetRandSeed),

the keyword "to" is an alias for "function".

the property is defined by the get & set functions pairs

the following is the Randomize function definition

// randomize

@function Randomize()
    aCriticalSection = @system.CriticalSection.RandSeedAccess.Create(),
    @CriticalSection.block (aCriticalSection) {
        // save sys randseed
        aSaveRandSeed = @system.RandomSeed,
        @try {
            // get updated randseed
            @selfvar.theRandSeed = @system.RandomSeed
        } finally {
            // restore sys randseed
            @system.RandomSeed = aSaveRandSeed

note that @system.RandomSeed is a static property that returns the Global RTL Random Seed var value

by default functions & properties are instance functions & properties

the following is the Random function definition

// random

@function Random()
    aCriticalSection = @system.CriticalSection.RandSeedAccess.Create(),
    @CriticalSection.block (aCriticalSection) {
        // save sys randseed
        aSaveRandSeed = @system.RandomSeed,
        // set our randseed
        @system.RandomSeed = @selfvar.theRandSeed,
        @try {
            @try {
                // check args
                if (@Args.Count() == 0) {
                    @return @Math.Random()
                } else if (@Args.Count() == 1) {
                    @return @Math.Random (@Args.Value(0))
                } else {
                    @return @Math.RandomRange (@Args.Value(0), @Args.Value(1))
            } finally {
                // get updated randseed
                @selfvar.theRandSeed = @system.RandomSeed
        } finally {
            // restore sys randseed
            @system.RandomSeed = aSaveRandSeed

note the use of @Math.Random (and so on) to compute the random value

this function uses the default random algorithm from Delphi RTL to compute the random value
after having set our private instance random seed.
this function properly saves & restores the system random seed
note the use of @Args.Count & @Args.Value, the are used to access the Actual Function Arguments
which are NOT Declared with names in the function head

Creating and Using a Scripted Object Example

to create a scripted object instance use the @new keyword
// create and init a new random object
aRand = @new (@AxMath.RandomObject).Initialize(),
to use our random object you may call the following
// make sure we got a true random value

// get a random value and store it into aVar
aVar = aRand.Random(),

here is a more complex example ...

@localenv {
    aCount = 3,
    @while (aCount > 0) {
        @system.out.WriteLine ( aRand.Random() ),
        -- aCount

the above code creates a local env in the stack and prints 3 random values on the std out.

note the use of the auto decr (--) unary operator

notes about namespaces

searches into named values list are optimized binary searches since they are kept sorted.
however if you are using a namespace repeatdly in some loops you may store it into a var for quick access
let's show an example
// store the Math namespace into a var
aMath = @Math,
    aMath.Sin (aValue),
the above example calls the Sin function stored into the @Math namespace

Using the VRCalc++ Engine in Delphi Applications

to use VRCalc++ Scripted Class Modules or to Execute a Main Script inside
a Package Enabled Delphi Application you need to
- Inizialize the VRCalc++ Engine Environment
- Load the Required Plug in Modules (included the ones provided by the Author)
- Load the Required Class Modules (included the ones provided by the Author)
to Run a Main Class Module you also need to create a Main Class Module and its Instance Objects
to Use the VRCalc++ Engine in your Application you need at least the following units ...
// Object Pascal // Delphi Code



    // VR Std Char Streams support
    YourApplicStdCharStmSup, // I/O std char streams implementation of your applic
    // may be a null impl if do not need them

    UVRGlobalFileDepsListResolverDefines, // contains the default file deps list file ext

    UVRSimpleGlobalPlugInPackageListManager, // plug in modules list mgr
    UVRSimpleGlobalPlugInPackageListDepsFileLoadUtils, // to load plug in modules
    // from a deps list file

    UVRCalcSXScriptDefinedModulesListDepsFileLoadUtils, // script def mods load utils
    // using a deps list file

    // VRCalc++ Engine Stuff




    UVRCalcSimpleValueTypesSupport, // just in the case you need object types convertions

    UVRCalcEngineFunctionType, // in the case you need to invoke scripted funcs
    // this is used to set the main module source file pathname

    // this is used to set the main module source file pathname

    // in the case you need a script defined module

    // Engine Module Script Executor
    UVRCalcEngineModuleScriptEvaluator, // in the case you need to run a main script mod

    // this is used to find a registered class

    // the following are used to create a class instance and invoke its methods

    // more uses of your application ...
after you need a procedure like this to Start Up the Environment ...
// start up env

procedure StartupVRCalcEngineExtendedEnvironment();
    // to do:
    // init console system - associate i/o streams

    // init global options

    // init the global vars list

    // init Thread Execution Env Instance

    // init the Global Named Values List

    // init the Global PlugIn Package List Manager
    // load plug in modules

    // writeln ('plug ins loaded');

      // load script defined modules
      // done
       on aExcept : Exception do begin
          ShowVRCalcRuntimeException (aExcept, ExceptAddr());

    // init VRCalc++ environ

    // get script file name

    // load script file

    // create main module // optional

    // create main module instance // optional
    // set up instance links

    SetAutoDisplayInstructionValueMode (true); // from the VRCalcSX console application
then you need a procedure to Shut Down the Env like this ...
// shut down env

procedure ShutdownVRCalcEngineExtendedEnvironment();
    aEnv : TVRCalcEngineExecutionEnvironment;
    SetAutoDisplayInstructionValueMode (false);

    // get env
    aEnv := GlobalThreadExecEnvInstance();

    // unload all script defined modules
    // nothing to do here - scripted modules are always unloaded
    // because they are reference counted

    // finally destroy the main execution VR-Calc engine env (if any) ...
    SetMainModuleInstance (nil);

    // finally destroy the main execution VRCalc++ engine global env (if any) ...
    SetMainModule (nil);

    aEnv.RunningModule := nil;
    aEnv.ErrorModule := nil;

    // terminate Global Named Values List

    // terminate Thread Execution Env Instance

    // terminate global vars list

    // terminate global options

    // unload all plug in modules

    // terminate the Global PlugIn Package List Manager

    // terminate console system

    // done!

call the start up engine ext env in your start up applic code

call the shut down engine ext env in your shut down applic code

now let's see in more detail ...

Set up and Terminate Std Char Streams

the following code is taken by the VRCalcSX (VRCalc++ Script Executor) Console Application.
for your Applic replace the actual type of the Reader and Writer as needed
// std i/o streams

// setup

procedure SetupStdCharIOStreams();
    aStdInReader : TVRCalcSXStdInputCharStreamReader;
    aStdOutWriter : TVRCalcSXStdOutputCharStreamWriter;
    aStdErrOutWriter : TVRCalcSXStdOutputCharStreamWriter;

    aStdInReader := TVRCalcSXStdInputCharStreamReader.Create();
    AddReferenceTo (aStdInReader);
        SetStdInReader (aStdInReader);
        ReleaseReferenceFrom (aStdInReader);

    aStdOutWriter := TVRCalcSXStdOutputCharStreamWriter.Create();
    AddReferenceTo (aStdOutWriter);
        SetStdOutWriter (aStdOutWriter);
        ReleaseReferenceFrom (aStdOutWriter);

    aStdErrOutWriter := TVRCalcSXStdOutputCharStreamWriter.Create();
    AddReferenceTo (aStdErrOutWriter);
        SetStdErrOutWriter (aStdErrOutWriter);
        ReleaseReferenceFrom (aStdErrOutWriter);


// shutdown

procedure ShutdownStdCharStreams();
    SetStdInReader (nil);
    SetStdOutWriter (nil);
    SetStdErrOutWriter (nil);

Loading Plug in Modules

to load the required Plug in Modules use something like this ...
// to load all plugin modules

procedure LoadAllPluginModules();
    aRootPlugInModulesDepsListFileName : string;
    // get root file name
    aRootPlugInModulesDepsListFileName := GetVRCalcSXPluginsListFilePathName();

    // load all plug in packages
    LoadAllSimplePlugInPackageModulesUsingDepsListFile (

the above code is taken from the VRCalcSX Console Applic

VRCalc++ Plug in Modules are responsible to Register the Exported External
Global Functions & Properties in some Namespace starting from the Global Root
which is accessible using the VRCalc++ At Operator (@)
so they can be accessed by Script Code

Loading Script Defined Modules

to load the required Script Defined Modules use something like this ...
// to load all script defined modules

procedure LoadAllScriptDefinedModules();
    aRootScriptDefinedModulesDepsListFileName : string;
    // get root file name
    aRootScriptDefinedModulesDepsListFileName := GetVRCalcSXScriptDefinedModulesListFilePathName();

    // load all
    LoadAllRequiredScriptedModulesUsingDepsListFile (

the above code is taken from the VRCalcSX Console Applic


Notes About Files Dependency List Files

Files Dependencies are stored in Files Deps List Files that are parsed so that a file is included only once.
the Default Deps List File Ext is "*.vrdeplst" and are basically text files.
they can contain end of line comments in the form ...
' this is a comment

// this is another comment
these files List the Dependecies of a File to Other Files
the Files Deps List File Parser resolve duplicates in File Names so a File is Parsed only once.
they can contain the following deps instructions ...
#use <filename> // parse a single file

#usefolder <folderpathname\*.ext> // use a folder contents

#usefoldertree <folderpathname\*.ext> // parse files of type *.ext in this and all subfolders

#include <depslistfilename> // include another deps list file
"filename" and "folderpathname" are relative to the folder where the deps list file is located
and can contain OS Env Vars Names in the form "%MyEnvVarName%"
the following functions use a Files Deps List File to parse Plug in as well as Scripted Modules
- LoadAllSimplePlugInPackageModulesUsingDepsListFile (aRootDepsListFileName)
    // to Load Plug in BPL Modules
- LoadAllRequiredScriptedModulesUsingDepsListFile (aRootDepsListFileName)
    // to Load Required Script Defined Modules
(*) see the Source Code & Docs for details

Using Files Deps List Files in a Scripted Main Module

VRCalc++ Scripted Main Modules have usually the "*.vrc" File Ext.
if a Main Script needs to use others Scripted Class Modules
(usually stored in files with the (*.vrcm) File Ext)
then the Main Script can have an Associated Files Dependencies List File.
the Associated Files Deps List File usually have the (*.vrdeplst) File Ext appended
to the Main Script File Name (including its file ext).
so if the Main Script File Name is "My Applic.vrc" then
its Associated Files Deps List File Name should be "My Applic.vrc.vrdeplst"
then in your Main Script Source (before using your classes) you have to invoke ...
// Load All Required Scripted Classes

this call uses by default your "My Applic.vrc.vrdeplst" File to Load the required Scripted Classes
you have specified inside it using (for example)
note: this function can have other parametrs to better specify what to load
but the defaults are sufficient for this example
#use <../User Lib/My Class.vrcm> // use a single file
#use <../User Lib/My Other Class.vrcm> // use a single file

#usefolder <../folderpathname/*.vrcm> // use a folder contents

#usefoldertree <../otherfolder/*.vrcm> ' use a folder and any subfolders contents
in turn if a "*.vrcm" file depends upon other files
it can have a "*.vrcm.vrdeplst" file associated
for example "../User Lib/My Class.vrcm" can have a "My Class.vrcm.vrdeplst" Associated File
that List its Dependencies to Other Files
the Files Deps List File Parser follows the Links Automatically in a recursive way
and ensures that a File is Parsed (in this case Loaded) Only Once.
(*) see VRCalc++ Code Examples and Source Code for more informations

Creating and Running a Main Scripted Module

to Run a Main Scripted Module you need something like this in your application ...

 // our VRCalc++ Engine Main Module

 var theMainModule : TObject; //TVRCalcScriptDefinedModule;

 var theMainModuleInstance : TObject; //TVRCalcScriptDefinedModuleInstance;

 // the global env

 // the main module

 function GetMainModule() : TObject;
     result := AddReferenceTo ( theMainModule );

 procedure SetMainModule (aModule : TObject);
     aOldModule : TObject;
     aOldModule := AddReferenceTo (theMainModule);
     if (aOldModule <> aModule) then begin
         AddReferenceTo (aModule);
         theMainModule := aModule;
         ReleaseReferenceFrom (aOldModule);
     ReleaseReferenceFrom (aOldModule);

 procedure CreateMainModule();
     aModule : TObject; //TVRCalcScriptDefinedModule;
     aModule := AddReferenceTo ( TVRCalcScriptDefinedModule.Create() );
         SetMainModule (aModule);
     ReleaseReferenceFrom (aModule);

 procedure SetupMainModule();
   // have nothing to do

 // the main module instance

 function GetMainModuleInstance() : TObject;
     result := AddReferenceTo ( theMainModuleInstance );

 procedure SetMainModuleInstance (aInstance : TObject);
     aOldInstance : TObject; // TVRCalcScriptDefinedModuleInstance;
     aOldInstance := AddReferenceTo (theMainModuleInstance);
     if (aOldInstance <> aInstance) then begin
         AddReferenceTo (aInstance);
         theMainModuleInstance := aInstance;
         ReleaseReferenceFrom (aOldInstance);
     ReleaseReferenceFrom (aOldInstance);

 procedure CreateMainModuleInstance();
     aInstance : TObject;
     aInstance := AddReferenceTo ( TVRCalcScriptDefinedModuleInstance.Create() );
         SetMainModuleInstance (aInstance);
     ReleaseReferenceFrom (aInstance);

 // set up links

 procedure SetupMainModuleInstance();
     aInstance : TObject; //TVRCalcScriptDefinedModuleInstance;
     aModule : TObject; //TVRCalcScriptDefinedModule;
     aInstance := GetMainModuleInstance();
     aModule := GetMainModule();
         SetModuleInstanceClassModule (aInstance, aModule);
         //aInstance.ClassModule := aModule;
         ReleaseReferenceFrom (aModule);
         ReleaseReferenceFrom (aInstance);

the above code is from the VRCalcSX Console Application

to Run our Main Module with the give instance you need a proc like this ...

// execute main script module instance

procedure ExecuteMainScript();
    aFileName : string;
    //aEnv : TVRCalcEngineExecutionEnvironment;
    aInstance : TObject; //TVRCalcScriptDefinedModuleInstance;
    aScriptText : string;
    aValue : TObject;
    aSavedCurrDir : string;
    aInstance := GetMainModuleInstance();
        aFileName := GetMainScriptedModuleFileName();

        // get script text from file
        //aScriptText := UVRTextFileSupport.GetTextFileNameContentAsString (aFileName);
        if (Length (aFileName) > 0) then
            // load script text file
            aScriptText := UVRTextFileSupportExt.LoadTextFileName (aFileName)
            // read std input
            aScriptText := UVRTextFileSupport.GetTextFileNameContentAsString (aFileName);
        //end if

        // set module script text
        SetModuleInstanceScriptText (aInstance, aScriptText);

        // [*] also set the source file pathname for the module

        //writeln ('about to run script');

        // save curr dir
        aSavedCurrDir := GetCurrentDir();
        // set curr dir to the one of the main module
              // run : try any evaluation ...
              aValue := EvaluateModuleInstance (aInstance);
              // ok!

              // the result value is always discarded
              ReleaseReferenceFrom (aValue);
              // ok!

              on aExcept : Exception do begin
                 // show exception
                 ShowVRCalcRuntimeException (aExcept, ExceptAddr());
            // restore old curr dir
            SetCurrentDir (aSavedCurrDir);

        // release the instance after use
        ReleaseReferenceFrom (aInstance);

the above code is from the VRCalcSX Console Application


Using Scripted Class Objects in a Delphi Application

in this section we use the "RandomObject" seen above as an example

in the interface of your class (maybe a Form) declare the following ...

// class TVRCalcClientTestAppMainForm

        // the random object

        private theRandomObject : TObject;

        public function GetRandomObject() : TObject;
        public procedure SetRandomObject (aObject : TObject);

        public property RandomObject : TObject
            read GetRandomObject
            write SetRandomObject;

        // to create the Random Object

        public procedure CreateRandomObject();


in the implementation section write the following ...


    // to Access our RandomObject

    function TVRCalcClientTestAppMainForm.GetRandomObject() : TObject;
        result := AddReferenceTo (self.theRandomObject)

    procedure TVRCalcClientTestAppMainForm.SetRandomObject (aObject : TObject);
        aOldObject : TObject;
        aOldObject := self.RandomObject;
        if (aObject <> aOldObject) then begin
            AddReferenceTo (aObject);
            self.theRandomObject := aObject;
            ReleaseReferenceFrom (aOldObject)
        ReleaseReferenceFrom (aOldObject)


    // to create the Random Object

    procedure TVRCalcClientTestAppMainForm.CreateRandomObject();
        aClass : TObject;
        aClassInstance : TObject;
      aValue : TObject;
        // get Random Object Class
        aClass := UVRCalcEngineGlobalNamedValuesListRegUtils.GetRegisteredClass (['AxMath', 'RandomObject']);
            aClassInstance := CreateClassInstanceOf (aClass);

                aValue := InvokeObjectDefinedNamedFunctionMethodOf (aClassInstance, 'Initialize', []);
                // discard result
                ReleaseReferenceFrom (aValue);
                // set new random object
                self.RandomObject := aClassInstance;
                // done

                ReleaseReferenceFrom (aClassInstance)

            ReleaseReferenceFrom (aClass)



also write the following declarations in the interface part of your Delphi class ...

// class TVRCalcClientTestAppMainForm

        // to test the Random Object

        public procedure RandomizeObject();

        public procedure TestRandomObject();

        public procedure TestRandomRange();

and in the implementation section write the following ...


   // randomize object

    procedure TVRCalcClientTestAppMainForm.RandomizeObject();
        aRandomObject : TObject;
        aValue : TObject;
        aRandomObject := self.RandomObject;
            aValue := InvokeObjectDefinedNamedFunctionMethodOf (aRandomObject, 'Randomize', []);
            ReleaseReferenceFrom (aValue);
            ReleaseReferenceFrom (aRandomObject)


    // to test the Random Object

    procedure TVRCalcClientTestAppMainForm.TestRandomObject();
        aRandomObject : TObject;
        aValue : TObject;
        aFloatValue : extended;
        aString : string;
        aRandomObject := self.RandomObject;
            aValue := InvokeObjectDefinedNamedFunctionMethodOf (aRandomObject, 'Random', []);
                aFloatValue := FloatValueOf (aValue);
                aString := FloatToStr (aFloatValue);
                // show result
                self.theResultStaticText.Caption := aString;
                // done
                ReleaseReferenceFrom (aValue)
            ReleaseReferenceFrom (aRandomObject)

    // Random Range (a, b)

    procedure TVRCalcClientTestAppMainForm.TestRandomRange();
        aArg1Value : TObject;
        aArg2Value : TObject;
        aRandomObject : TObject;
        aValueResult : TObject;
        aResult : integer;
        aString : string;
        aArg1Value := CreateIntegerNumberValue (self.theRandomRangeMinSpinEdit.Value);
            aArg2Value := CreateIntegerNumberValue (self.theRandomRangeMaxSpinEdit.Value);
                // get Random Object
                aRandomObject := self.RandomObject;
                    // call function
                    aValueResult := InvokeObjectDefinedNamedFunctionMethodOf (aRandomObject, 'Random', [aArg1Value, aArg2Value]);
                        // get result
                        aResult := UncheckedIntegerValueOf (aValueResult);
                        aString := IntToStr (aResult);
                        // show result
                        self.theRndRngResultStaticText.Caption := aString;
                        // done
                        ReleaseReferenceFrom (aValueResult)
                    ReleaseReferenceFrom (aRandomObject)
                ReleaseReferenceFrom (aArg2Value)
            ReleaseReferenceFrom (aArg1Value)



after in your FormCreate method write the following ...


// create random object


then associate Click Event Handlers to the procedures shown above


VRCalc++ OOSL modifications history

[*] the first (non object oriented) prototype version (derived from tinycalc and xcalc) was started on March 2006 featuring many characters of the final version including proc flow control structs although it was non object oriented and capable only to handle floating point numeric data, also the console was initially integrated with the runtime engine and to add a feature you had to recompile all


[*] the final object oriented version 2.0 was started on March 2009 and completed at end of 2009 (including proceduiral flow control script parsers) after having rewritten most of the Delphi code of the 2006 version to adapt it to Reference Counted Interfaced Objects (* see Inside VRCalc++ docs, COM Interfaces) and after having isolated the VRCalc++ Engine Code in their proper packages.

also note that, because the use of object interfaces on which VRCalc++ symbolic operators are based on, support for operators overloading is also available and it will be extended in future releases.


[*] partial support for many RTL objects and functions [Math, DateTime, and so on …] was added to final version in 2010 as long as support to access many Engine features [VRCalcEngineExtension]


[*] the Inside VRCalc++ documentation was started on Feb 2015 as a set of RTF docs


[*] many built in Engine Classes were improved on March 2015 including the EngineThread Class that now supports Runnable Objects as well as Runnable Scripted Module Instances and some generic interfaces were added to support Scripted Modules

 [*] support for runtime Required Scripted Modules Loading Optimization was also added on March 2015 using module dependencies list files with a generic load modules engine and a couple of applets to smart edit the deps list files thus preventing loading required modules 2 or more times in a row and also resolving any circular refs among modules


[*] although the VRCalc++ Engine was designed to work mainly with Reference Counted Interfaced Objects (* see COM Interfaces, Inside VRCalc++ docs), support for using simple not ref counted objects (such as RTL TList objects) is also provided from April 2015 and support for optional module termination callback scripted methods was added, it is needed in the case you work with these objects to perform the required clean up (* see Inside VRCalc++ docs for details). pluggable functions to work with some RTL System Classes (TList, TStrings, and so on …) were also added


[*] as a result of the above, from April 2015 the list of VRCalc++ Engine Built in functions and script parsers was enlarged and improved by including many of the most important proc flow control structs and functions to access the runtime environment (before from 2009 they were defined only into an external package to plug in separately, now they are part of the Base Engine Module Package). support for global vars was also added along with some improvements of the VRCalc++ Engine Thread Execution Global Environment


[*] on April 2015 a pair of expressions operators and engine properties were added to optimize expressions evaluations in both time & space. one of the most important optimization is the one that allow us to eval an expression using flat operators with no implicit precedence among them, it is up to the programmer to set operators precedence in expressions by using the required blocks enclosing here the expressions with higher eval precedence. this option allow us to save evaluation time & stack space


[*] on Aug 2015 format of Deps List Files was improved to support relative pathnames, include files, env vars expansion, directories and sub-folders recursion and user comments by modifying the relative sintax. also their use has been extended to plugglabe (BPL) modules used to extend the language functions.


[*] on Nov 2015 the basic math operators functions were upgraded to support integer math operations side by side to floating point math with automatic convertions, that is all basic math is performed at highest precision for both unsigned and signed integral numbers and extended floating point numbers. format of numeric costants was extended to support unsigned and signed integrals and floating point numeric constants using type suffixes while remaining backward compatible.


[*] also the VRCalc++ Console (* see below) was improved from Feb 2015 and a new console only executor of VRCalc++ scripts was created on Feb 2015 [VRCalcSX]


[*] on Sep 2016 a simple Test Applic Delphi Project embedding the VRCalc++ Engine was created to demonstrate Scripted Object of a Class Module Creation and Method Invocation (it uses a scripted RandomObject to generate Random Values).


[*] on Dec 2016 some non-interfaced objects with copy semantics (not reference counted) were created and added to the main library of live objects along with support functions to store simple data types without computing interfaces to save memory space (by default a simple data type (string, integer, float, …) is created for computing purposes so it implements all the required interfaces to make computations and its size is very large). you cannot perform computations on simple storage objects of this type, you have to convert them to computing object implementing required interfaces to perform computations. their purpose is only to save space for storage.


[*] on Feb 2017 functions and properties declarations were improved by introducing new keywords (“public”, “private”, “pure”). VRCalc++ Console now shows error line & col.


[*] on Dec 2018 some useful scripted classes (such as: ArrayList, ListIterator, SubList, RandomObject, Observable Support …) along with useful scripted List utils (such as: QuickSort, BinarySearch …) and some useful scripted objects (such as: VRStd.In, VRStd.Out …) were added to the Auto-Load Scripted Modules List.
other VRCalc++ Demos based on these classes and procs were added.
the new keyword “@abstract” was added to mark a scripted function as Abstract, put it inside an abstract method function to override then it raises an exception if that method function gets called.

VRCalc++ Links

you may find VRCalc++ OOSL Source Code and Delphi Build Projects
along with some other Delphi Projects & Source Code by Vincent Radio in
at the following address ...
that's all folks ...



This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

No Biography provided

You may also be interested in...


Comments and Discussions

GeneralMessage Closed Pin
26-Dec-18 2:09
memberMember 1410061726-Dec-18 2:09 
GeneralMy vote of 5 Pin
Vincent Radio18-Dec-18 7:12
memberVincent Radio18-Dec-18 7:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06 | 2.8.190114.1 | Last Updated 27 Dec 2018
Article Copyright 2018 by Vincent Radio
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid