Click here to Skip to main content
14,599,797 members

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

Rate this:
3.45 (9 votes)
Please Sign up or sign in to vote.
3.45 (9 votes)
30 Jul 2020CPOL
VRCalc++ Engine is embeddable in any Delphi Application using Dynamic Packages
VRCalc++ is an Object Oriented Scripting Language which interpreter is embeddable in any Delphi Application using Delphi Packages.


Born as a simple calculator, VRCalc++ features the same operators as C++ and Java and 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 the following:

  • access to the Delphi 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 independent 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:


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 separated by the comma symbol (,) and can be enclosed in normal (...) or curly {...} blocks. There are 3 types of code blocks:

  • (...) : rounded 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. Then, you may also specify the exponent using (E | e) an optional sign (+|-) and an int number. Then, 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 specified 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

Then, you may convert the result using the provided functions.

VRCalc++ Variables

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)

You may create a local environment, then a variable name in the local env hides the variable with the same name in the outer env.

For example ...

   a = 0,
   @localenv {
      @var a = 2.5 // this is a new var the above "a" is hidden here
   @VRStd.Out << a

the above code will print "0" on the std out channel.

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

or you may write ...

a = 5,
b = @integer (a) // 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

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 define:

  • 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. The VRCalc++ Engine was updated so now Lambda Functions are supported 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 function 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 extend 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 of named objects and that is also a Named Object.

VRCalc++ Plug In Modules

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 type
  • / : divide
  • * : mult
  • div, \ : int divide
  • mod, % : modulus (remainder of an integral division)
  • + : addition
  • - : subtraction
  • & : bit and
  • | : bit or
  • ^ : bit xor
  • <<, >> : shift left, shift right (overridden and also used for insertions & extractions)
  • && : bool and
  • || : bool or

Some unary prefixed 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 overridden 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 ...

Function, Property and Variables definition

  • var used to define a variable inside a context
  • property used to define a property
  • on used to define a function or procedure
  • to same as above
  • function used to define a function
  • func same as above
  • procedure used to define a procedure
  • proc same as above

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 using an internal iterator
  • return : to return a value from a function
  • label : to define 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 Useful 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
    UsingOption // using option to capture local var values by name
    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 separated list of formal args names (it can be empty). Args names follow the same rules for named objects (* see above).

UsingOption to capture var values by name. This allow the use of Lambda Functions.

Its syntax is: [using | capture] [ varname1, varname2, varname3, ... ]. See docs and samples for more information.

The capture option is often used with no name, static, private function definitions.
The captured vars values are the ones when the function is defined.
The captured vars should be local vars.

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 invocation)

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 separated 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 it's only accessible by its ref value.
  • public: The property is registered into the class and it is publicly 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 operator and its ref can be dereferenced using the (*) unary operator.

Note that variables created inside a module are "private" to that module, however you may access them using the provided functions that get the context of a module or a module instance.

VRCalc++ Scripted Class Registration Example

Suppose you have 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 "Random". Then it creates a name space into the Root named "VRMath" (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 in the end, it registers itself into "@root.VRMath".

Here is the code: Random,

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

@if (
    @classmodule.IsNameRegisteredInto ( @root.VRMath )
) 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.VRMath ),

Defining a Function Example

Let's show some function definitions using the Random example. The following is our Random Object constructor, its function name is "Initialize" and takes no args, it creates an instance var named "theRandSeed" and sets 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 instance 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 are used to access the Actual Function Arguments which are NOT declared with names in the function head.

Using Lambda Function Samples

These are some examples of Lambda Functions definition and usage ...

@function CallFuncArg (aFunc) static
    @VRStd.Out.WriteLine ("calling a function by addr ..."),

@function TestLambdaFunction() static
    aMessage = "this is a message text !!",

    aCount = 5,
    aIndex = 0,
    @while (aIndex < aCount) {

        @thisclass.CallFuncArg (
            @function () capture [aMessage, aIndex] static private {
                @VRStd.Out.WriteLine ("message is : " + aMessage + " :: " + aIndex)

        ++ aIndex

    @VRStd.Out.WriteLine ("done")

@proc main() static


There is another example of lambdas usage: a function that returns a lambda function ...

@function ReturnAdder (aXValue) static
    @function (aYValue) capture [aXValue] static private
        aXValue + aYValue

aAdderFunc = @$ReturnAdder (5),

aAdderFunc (1)

The above would print the value 6 on the std out channel.

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 (@VRMath.Random).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 repeatedly 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.


VRCalc++ Language Code Samples

this is a Guess a Number in the Range [0 .. N] Console sample ...


@to PlayGame (aMaxValue) static
    aRnd = @new (@VRMath.Random).Initialize(),

    aNumberToGuess = aRnd.RandomRange (0, aMaxValue + 1),

    aMaxNumOfTries = @VRIntMath.Utils.IntLog (aMaxValue, 2) + 1,

    aTryCount = 0,

    @repeat {

        @if (aTryCount >= aMaxNumOfTries) { @break },

        @VRStd.Out << "you have " << (aMaxNumOfTries - aTryCount) << " tries left" << @std.endl,

        @VRStd.Out << "try #" << aTryCount + 1 << @std.endl,

        @VRStd.Out << "enter a number in the range [" << 0 << " .. " << aMaxValue << "]: ",
        aStr = @VRStd.In.ReadStringLine(),
        aNumber = @String.Parse.AsInt (aStr),

        if (aNumber < aNumberToGuess) {

            @VRStd.Out << "retry, the number to guess is > than " << aNumber << @std.endl,

        if (aNumber > aNumberToGuess) {

            @VRStd.Out << "retry, the number to guess is < than " << aNumber << @std.endl,

        else {

            @VRStd.Out << "ok (" << aNumberToGuess << " == " << aNumber << ")" << " you win !!!" << @std.endl,



        ++ aTryCount

    @VRStd.Out << "sorry, you loose !!" << @std.endl,

@proc Main() static
    @VRStd.Out << "Guess a Number" << @std.endl,

    @VRStd.Out << "enter max value: ",
    aStr = @VRStd.In.ReadStringLine(),
    aMaxValue = @String.Parse.AsInt (aStr),

    @VRStd.Out << "Guess a Number in the range [" << 0 << " .. " << aMaxValue << "]" << @std.endl,

    @thisclass.PlayGame (aMaxValue)



note: tu run this script properly you need to load the VRCalc++ VR System Std Runtime Library


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:

  • initialize 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 conversions

    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 ...

Then, 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 :=

    // 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 Dependencies 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 has 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 parameters 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 Lists 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 information.

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 given 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 
                           (['VRMath', 'Random']);
            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)


Then, 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 only capable of handling 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 procedural 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 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

[*] In 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 optimizations 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 allows us to save evaluation time & stack space

[*] In 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 syntax. Also, their use has been extended to pluggable (BPL) modules used to extend the language functions.

[*] In Nov 2015, the basic math operators functions were upgraded to support integer math operations side by side to floating point math with automatic conversions, 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 constants 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]

[*] In 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).

[*] In 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.

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

[*] In 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.

[*] In Jan 2019, support for Paged Arrays (based on VRPagedLists & Iterators) was added to VRCalc++ along with Scripted Classes for Lists based upon Paged Arrays.

[*] In Feb 2019, some useful Object Array Functions were added to the Extension Package including Reverse, BinarySearch, QuickSort, RelocateItems, RotateItems, and so on.

[*] In Nov 2019, scripted functions definitions were updated to support Lambda Functions that can capture the environment variables values which name you may specify in a using clause (* see docs for more details); this is often used with static private functions which usually have no name.
The keywords (strong, strongref, weak, weakref) were added in variables definition.

VRCalc++ Links

You may find VRCalc++ OOSL Source Code and Delphi Build Projects along with some other Delphi Projects & Source Code by Vincent Radio on at the following link:

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

Comments and Discussions

Questionsame kind in .net Pin
Member 127077526-Jul-20 11:29
MemberMember 127077526-Jul-20 11:29 
GeneralMy vote of 5 Pin
Vincent Radio18-Dec-18 6:12
MemberVincent Radio18-Dec-18 6: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.

Posted 18 Dec 2018


5 bookmarked