|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Big News in This ReleaseTOOLForge, the IDE for TOOL, has been enhanced to allow visual creation of XML Forms. This enhancement will save anyone interested in using XMLForms with TOOL the pain of creating form definition files by hand. There is more work to be done here with the more advanced XMLForm control types, but I'm wagering the existing feature set will address most user's needs. However, this program can only be released in non-source form. There is just too much commercial library code in this project to have it any other way. See below for more information on how to work with TOOLForge to debug TOOL scripts. IntroductionTOOL (Tiny Object Oriented Language) is an easily-embedded, object-oriented, C++-like-language interpreter. The language, and indeed a significant part of the core of the TOOL engine, is based on the BOB project, a work that was originally developed by David Betz covered in previously published issues of Dr. Dobb's Journal. The original language interpreter was implemented entirely in K and R style C. The TOOL interpreter uses an object-oriented design of the original BOB interpreter, and also includes countless dozens of other enhancements to the original project. The object oriented redesign was accomplished by packaging the functionality of the original project into a small set of inter-operable C++ classes. These classes were then aggregated into a single container class that acts as a facade over the entire system. One of the primary purposes for the redesign of the original interpreter was to bring the code up to “more modern standards”. However, the key benefit of packaging the interpreter into a single container class is that any application can easily create an "interpreter context" simply by invoking a single class. Applications can also run multiple "stand-alone" interpreter sessions simultaneously simply by having multiple instances of the wrapper-class in scope at once. The purpose of this article is to introduce the TOOL interpreter and language from the perspective of a person who has a desire to include a scripting solution as part of his project. To this end, the following topics will be discussed:
These days, interest in interpreters seems to be at an all time high, and there are many interpreters and technologies to choose from. This being the case, why should anyone consider the TOOL engine as part of a project’s solution? A short list of considerations follows below, which have driven me to include TOOL as part of my own products.
The design of the C++ classes that make up the TOOLAs was mentioned, TOOL is comprised of a small number of core classes. The bulk of the files included in this project are extensions to that basic engine. Since there is not enough space to cover all these classes in great depth, I’ll have to leave the extended analysis to the reader, and therefore my goal will be to attempt to impart an understanding of the core classes within the TOOL engine. I hope to give you enough information to allow you to get your bearings in the project; but down in the guts understanding is best gained by wrestling the project to the mat. Listing 1. Simple example of how to drive the TOOL engine. What we are shown here is a data exchange variable called “ int main(int argc, char* argv[]) { SCRIPT_ARGUMENTS oArgs; oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestVector" ) , new CAppContextVariant( "On" ) ) ); oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestStack" ) , new CAppContextVariant( "On" ) ) ); oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestQueue" ) , new CAppContextVariant( "On" ) ) ); oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestMap" ) , new CAppContextVariant( "On" ) ) ); oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestByteArray" ) , new CAppContextVariant( "On" ) ) ); oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestString" ) , new CAppContextVariant( "On" ) ) ); VMFacade* poScript = new VMFacade(); poScript->SetVar( &oArgs ); poScript->CompileFile( "C:\\Projects\\ScriptEngineTester\\CoreClasses.tool" ); poScript->Execute( "TestAll" ); return( 0 ); } The first class that the users of TOOL are going to encounter is the If you need to share variables from your program with the script engine, or if you need to retrieve results from the execution of a script, you can easily accomplish this by placing parameters/variables into an instance of a The script can pull values out of the container by name while it is running using a call to the TOOL API function: In order to facilitate communication between the hosting process and the scripting environment then: simply declare an instance of a With all this setup being accomplished, the next thing to do is to invoke the scripting engine with a call to the I’ve used both approaches in my projects. The first approach is handy if you would like to have multiple entry points (say for example, a kind of a macro language system available to your project) in a single script file. The second approach is handy if you want to describe a set of standalone script files that all will be invoked by the hosting program in the same fashion as “scripted objects”. In my case, I’ve employed an Init/Run semantic quite successfully wherein all scripts must have Peeking behind the Part of the reason for the deep level of “cross connection” between the major TOOL components is historical, in that the original design and implementation of TOOL was based on the BOB engine. Even though the major objects were designed based on broad functional areas, there were some areas where the separation could not be performed completely (just the same as in one cannot completely separate memory from the CPU in a “real” computer system). Even so, I’m hopeful that you will find my repackaging of the BOB project in to this particular set of classes is sensible and cohesive. Looking more closely at what is contained in the At the next level out, there is the Still continuing on our journey out from the TOOL core, you’ll see that there is a At the highest level of TOOL, is the How does the TOOL Engine work?TOOL is implemented as a hybrid of a compiler and an interpreter (an inter-piler?). When a function is defined, it is compiled into instructions for a stack-oriented, byte-code machine. When the function is invoked, those byte-code instructions are interpreted. The advantage of this approach over a straight interpreter is that syntax analysis is done only once, at compile time. This speeds up function execution considerably and opens up the possibility of building a run-time-only system that doesn't include the compiler at all. In fact, in some of my scripts (which are not overly complex, but are not all that simple either), I’ve gotten over 150 complete executions a second through the system. Your mileage may vary of course, but TOOL, while nowhere near as fast as compiled code is no slouch either. The virtual machine that executes the byte-codes generated by the TOOL compiler has a set of registers, a stack, and a heap. These constructs are contained in the In TOOL script classes, all member functions are virtual. This means that when a member function is invoked, the interpreter must determine which implementation of the member function to invoke. This is done by the TOOL variable types have been extended to support the following basic data types: Longs, Doubles, Bytes, Strings, Tokenizers, ByteArrays, Vectors, Classes, DateTimes, Queues, Maps, Stacks, WaitObjects, NT Kernel-handles, DWORDs, NT-File handles, File-Find handles, zib-files, SQLite databases, ODBC connections, and nil. Internally, the interpreter uses four more types: classes, compiled bytecode functions, built-in function headers, and variables. Wherever a value can be stored, a tag indicates the type of value presently stored there. This is the “ Objects, vectors, and bytecode objects are all represented by an array of value structures. In the case of bytecode objects, the first element in the vector is a pointer to the string of bytecodes for the function, and the rest are the literals referred to by the bytecode instructions. Class objects are vectors, where the first element is a pointer to the class object and the remaining elements are the values of the nonstatic member variables for the object. Built-in functions are just pointers to the C functions that implement the built-in function. Variables are pointers to dictionary entries for the variable. There is a dictionary for global symbols and one for classes. Each class also has a dictionary for data members and member functions. In addition to the stack, TOOL uses a heap to store objects, vectors, and strings. The current implementation of TOOL uses the C heap and the C functions Operator OverloadingIn the TOOL interpreter, an operator can have several different meanings based on the LHS/RHS values for the operator. Consider the case of the array index operator “ Function OverloadingIn the TOOL API, intrinsic functions can be “overloaded” in that they can be coded to accept different parameter counts and types. One example of this (and there are many others) is the The most extreme implementation of this feature is found in the function This flexibility in defining functions is a natural outcome of the way the interpreter pushes function arguments on to the stack. The interpreter has no information regarding the number and type of arguments needed by any intrinsic function. It will simply push all arguments found for the call on to the stack. This feature enables any intrinsic function to be coded to accept all types of arguments from the interpreter. There is a “flipside” to this flexibility however, and that is that all intrinsic functions should perform sanity checks on all arguments and argument counts to verify that what it is about to operate on is what is expected in the function. All Ashore, That’s Going AshoreWell that’s about a whirlwind of a tour that I can provide without getting completely bogged down in all the details that bring TOOL together. Hopefully, I have provided enough of an introduction to the lay of the land in the TOOL engine so that you can have some solid ground to stand on while you analyze the inner workings of TOOL. However, take comfort in knowing that you can easily use and extend TOOL without knowing how all the inside pieces and parts work. I just thought I’d show your around the project a bit, so that when you go to kick the tires on this beasty yourself, you’ll know where to find the fenders. Programming in TOOLThe examples below present a simple example program; a function for computing factorials written using the TOOL language. Note: Readers are also encouraged to review the sample scripts included with this distribution. // factorial program // Factorial( iValue ) { return( ( iValue == 1 ) ? 1 : ( iValue * Factorial( iValue - 1 ) ) ); } Run( ;iLoop ) { iLoop = Long( 0 ); for ( iLoop = 1; iLoop < 10 iLoop++ ) { Echo( StringFormat( "Factorial for % is %", iLoop, Factorial( iLoop ) ) ); } } You can see that this program looks a lot like its C counterpart. The only noticeable difference is the lack of a declaration for the type of the parameter Other points worth noticing in this first sample are the variables declared in the Important note: Even though TOOL is a weakly typed language from the script writer's perspective, the TOOL interpreter will type-check the type of a variable passed into its routines. If the variable is not of the right type, the interpreter will halt the execution of the script. Again, what you should notice primarily is how much the example program above looks a lot like a similar program written in C. Also, notice that the program uses the In addition to supporting C-like expressions and control constructs, TOOL also supports C++-like classes. Again, since TOOL is a typeless language, the syntax for class definitions is somewhat different from C++, but it is similar enough that it should be easy to move from one to the other. The next example shows a simple class definition that defines a class called // class declaration // class Foo { m_A; m_B; static m_Last; static GetLast(); } Foo::Foo( AValue, BValue ) { m_A = AValue; m_B = BValue; m_Last = this; return( this ); } Foo::GetLast() { return( m_Last ); } As in C++, new objects of a class are initialized using a constructor function, which has the same name as the class itself. In this example, the constructor takes two arguments, which are the initial values for the member variables In TOOL, all class data members are implicitly protected. The only way to access or modify the value of a member variable is through a member function. If you need to access a member variable outside a member function, you must provide access to member functions to do this. We'll continue to refine the // continuing to define the Foo class // Foo::GetAValue() { return( m_A ); } Foo::GetBValue() { return( m_B ); } Foo::SetAvalue( NewAValue ) { m_A = NewAValue; } Foo::SetBValue( NewBValue ) { m_B = NewBValue; } Foo::GetSpan() { return( m_B - m_A ); } Run( ;poFoo1, poFoo2 ) { poFoo1 = new Foo( 1 , 2 ); poFoo2 = new Foo( 11, 22 ); Echo( "Foo1 Span Is: " + poFoo1->GetSpan() ); Echo( "Foo2 Span Is: " + poFoo2->GetSpan() ); } TOOL also supports an inheritance model similar to the Java language, in that it allows one class to be derived from another. The derived class will inherit the behavior of the base class and possibly add some behavior of its own. TOOL only supports single inheritance; therefore, each class can have at most one base class. The next code example defines a class The class // class derivation in TOOL // class Bar : Foo { m_C; } Bar::Bar( AValue, BValue, CValue ) { Foo*->Foo( AValue, BValue ); m_C = CValue; return( this ); } This example illustrates another difference between TOOL and C++ objects. In C++, constructor functions cannot be called to initialize already existing objects. This is allowed in TOOL, however, so the A Quick Mention of Programming StyleSince TOOL is a weakly typed language, meaning that the type of variables is not required to be specified, I found it useful to use some means of classifying the data-type of a script variable (for the purpose of making the code more understandable to others). For this reason, a simplified form of Hungarian Notation is used in all script samples included with this article. Following are samples of the notation used: Data Type Hungarian "Marker" Example
String s sMyString
Number i iValue
Object o oValue
I don’t mean to promote Hungarian Notation in any specific way, as I know that just as many folks don’t like it as I do, I simply find it to be a useful when striving to understand large projects. More on a Suggested Programming StyleTOOL is a weakly typed language meaning that the type of variables does not have to be specified. Therefore, some means of classifying the data stored in a variable (for the purpose of making the code more understandable to others) would be of great benefit. Therefore, a simplified form of Hungarian Notation is encouraged in all TOOL program scripts. Following are samples of the notation used:
Creating and Allocating TOOL VariablesThere are several types of variables in TOOL. They are all listed below along with the TOOL call for creating the variable. Readers familiar with C++/C#/Java can think of these API calls as calls to a constructor for a variable. Vector(); // accepts no arguments String( 255 ); // reserve 255 characters for length String( "Hello" ); // create and assign value to string Queue(); // accepts no arguments Stack(); // accepts no arguments Map(); // accepts no arguments Tokenizer( sToTokenize, sDelimiter ); ByteArray(); // create byte array with default parameters ByteArray( Long( 25 ) ); // create byte array with 25 elements ByteArray( Long( 25 ), Long( 5 ) ); // create byte array with 25 // elements and grow factor of 5 // elements Long(); // create a long with value zero Long( 50 ); // assign value to a long variable Handle( 0 ); // create a handle type MUST have init value DateTime(); // create a datetime value equal to 'now' DateTime( dtOther ); // copy construct a date time variable DateTime( 2004, 01, 01, 12, 15, 0 ); // create date time with init // value of 01/01/2004 12:15:00 Color(); // create color with RGB 255,255,255 Color( 0, 0, 0, ); // create color with RGB 0,0,0 Color( clrOther ); // copy construct a color variable DWORD(); // create a DWORD with value of zero DWORD( dwOther ); // copy construct a DWORD variable DWORD( Long( 5 ) ); // construct a DWORD from a number value Database(); // create an ODBC database variable Calculator(); // create an calculator / equation handler Double(); // create a double with a value of 0.00 Double( Long( 6 ) ); // create a double from a long value Double( dblOther ); // copy construct a double variable Double( "123.45" ); // create a double from a string value Byte(); // create a byte initialized to zero/false Byte( bOther ); // copy construct a byte type variable Byte( Long( 10 ) ); // set byte variable from number // NOTE: limit of 0 - 255 for the number Page(); // report page object Table(); // table object in a report page Color(); // color object in a report page Database(); // odbc database connection object Calculator(); // function evaluation object NullValue(); // TOOL NULL Value MiniDatabase(); // SQL LITE Wrapper Object UnZipper(); // zlib Wrapper Object for unzip operations ZipMaker(); // zkib Wrapper Object for zip operations FileBuilder(); // string collection object for creating file output DelimitedFile(); // wrapper object for delimited file I/O ExcelExporter(); // Wrapper for an excel workbook export object TOOL Run Time Type CheckingSince TOOL is a relatively weakly-typed language, as was explained previously in another topic; while at the same time the TOOL interpreter validates that all arguments passed to the TOOL API functions are of the proper type, TOOL offers an entire suite of RTTI (Run Time Type Identification) functions for script writers in order to assist them in writing solid and reliable script programs. Use of these functions allows the creation of more robust scripts due to the argument validations that can be performed on all variables. An additional use of these functions may be to write scripts that will conditionally branch based on the type of variable being tested. There will be examples of both types of usage later in this document. The complete list of RTTI functions is listed below: IsNull( oVarToTest ); // is the variable NULL? IsClass( oVarToTest ); // is the variable a TOOL script class? IsVector( oVarToTest ); // is the variable a vector type? IsNumber( oVarToTest ); // is the variable a long number? IsString( oVarToTest ); // is the variable a string? IsFile( oVarToTest ); // is the variable a file? IsKernelObject( oVarToTest ); // is the variable an NT kernel object? IsHandle( oVarToTest ); // is the variable an NT style handle? IsDateTime( oVarToTest ); // is the variable a TOOL date time type? IsDWord( oVarToTest ); // is the variable a DWORD? IsNTHandle( oVarToTest ); // is the variable an NT handle? IsFindHandle( oVarToTest ); // is the variable a "file find handle"? IsQueue( oVarToTest ); // is the variable a TOOL queue type? IsStack( oVarToTest ); // is the variable a TOOL stack type? IsHashTable( oVarToTest ); // is the variable a TOOL map type? IsConstant( oVarToTest ); // is the variable declared as const? IsConstCast( oVarToTest ); // is the variable cast as const? IsDouble( oVarToTest ); // is the variable a double? IsByte( oVarToTest ); // is the variable a byte? IsByteArray( oVarToTest ); // is the variable a byte array type? IsDatabase( oVarToTest ); // is the variable a database connection? IsReportPage( oVarToTest ); // is the variable a report page? IsPageRegion( oVarToTest ); // is the variable a report page region? IsPageTable( oVarToTest ); // is the variable a report page table? IsColor( oVarToTest ); // is the variable a report page color? IsMiniDatabase( oVarToTest ); // is the variable a sqlite database wrapper? IsUnZipper( oVarToTest ); // is the variable a zlib unzipper wrapper? IsZipMaker( oVarToTest ); // is the variable a zlib zipper wrapper? IsMiniRowSet( oVarToTest ); // is the variable a SQLite rowset wrapper? IsFileBuilder( oVarToTest ); // is the variable a file builder object? IsDelimitedFile( oVarToTest ); // is the variable a delimited file I/O wrapper? IsExcelExporter( oVarToTest ); // is the variable an Excel workbook // creator wrapper? Note: See the class TOOL Date Time OperationsData processing and reporting often needs to deal with dates or date ranges. In fact, date time processing is likely to be "second most common" for data processing/reporting data types, with only string data being more frequently used. Because DateTimes are such a common requirement, TOOL offers a built in data type for that type of value; and also offers a set of API functions that offer the most common types of operations used for DateTime based values. These functions are listed below and a class wrapper for this family of functions can be found in the class called dtNow = GetDate(); dtLater = DateAdd( sModPart, dtBase, iValue ); dtEarly = DateSub( sModPart, dtBase, iValue ); iPart = DatePart( sDatePart ); dtNew = DateSetHourPart( dtToChange, iHour ); dtNew = DateSetMinsPart( dtToChange, iMins ); dtNew = DateSetSecsPart( dtToChange, iSecs ); dtNew = DateSetHMS( dtToChange, iHour, iMins, iSecs ); sText = DateToString( dtDate ); dtNew = StringToDate( sText ); bLess = DateCompare( dtBase, dtOther ); iSpan = DateDiff( dtBase, dtOther ); TOOL File and Directory OperationsDirectory Related FunctionsDirectory operations are very common for certain classes of data processing applications. To enable these types of jobs, TOOL includes several functions specific to those related tasks. The following classes illustrate the use of these types of functions. The first TOOL class to study for this section of the TOOL API is found in the ToolBox.Tool file called The next directory related TOOL class wraps all those functions that actually manipulate the directory structure of a disk. There are TOOL API functions for moving, copying, comparing, renaming, and deleting entire directory trees on hard disks. See the Directory and File Traversal Related FunctionsCertain data transport tasks need to periodically scan file and directory structures as part of their processing. To support these types of jobs, TOOL offers a set of functions for traversal of directories and files. Class wrappers for these types of functions can be found in the ToolBox.Tool file in the For a practical example that uses these classes, refer to the simple network file backup program found in the TOOL Network Drive Operations topic. That example will demonstrate how TOOL can be put to use for purposes related to system administration in addition to tasks dedicated to data I/O, transformation and reporting. The fact that TOOL can be used for various tasks outside of strictly data transport and reporting, adds additional value to the TOOL language in that the TOOL system has enough facilities included that allow it to be used for all types of operations. File Related FunctionsThe TOOL API also offers file related functions for working with system files. TOOL provides a set of functions for inspecting file attributes, another set of functions for performing both text-based file I/O and for serialization of TOOL variables, ini-file I/O and file parsing. Classes that illustrate each of these portions of the API can be found in the ToolBox.Tool file. See the Finally, see the Note, that at this time, collections of data (stacks, queues, maps, etc.) can not be serialized automatically. However, if demand warrants this type of functionality, it can easily be added to the TOOL run-time engine. The final specialized area of TOOL file I/O processing is with regards to INI file processing. Since INI files are a convenient and easy to use method of storing simple hierarchies of configuration data, they are still a prevalent method of storing application parameters. So, as a convenience, TOOL also offers a set of functions for INI files. A class that provides a wrapper over these API functions is the TOOL Network Drive OperationsTOOL offers a pair of functions that are related to connecting to network drives. There is one function to connect to a networked shared directory and another to disconnect from the same. The sample class named In the sDriveLetter = "ERROR"; This shows another overloaded operator for string operations where TOOL string type variables can be directly tested for equality in addition to the Run()
{
CNetworkDrive poNetOut = new CNetworkDrive();
// since the '\' character is an escape character within strings
// i.e., \t for tab
// \r carriage-return
// \n for line-feed
// \" for embedded-quote
//
// we need to use \\ for each \ character that should be made
// part of a string
//
// so in the string below: "\\\\MyServer\\BackupShare"
// will resolve to: "\\MyServer\BackupShare"
//
if ( poNetOut->Connect( "\\\\MyServer\\BackupShare",
"Admin",
"Password" )
{
sNetDrive = poNetOut->GetDriveLetter();
// this example will back up three other computers to
// the single backup server
//
for ( int iLoop = 0; iLoop < 2; iLoop++ )
{
// based on the machine being backed up, set up for
// that copy operation
//
if ( iLoop == 0 )
{
BackUpSingleServer( sNetDrive,
"Workstatation1",
"ImportantWork" );
}
else
if ( iLoop == 1 )
{
BackUpSingleServer( sNetDrive,
"Workstatation2",
"FinanceData" );
}
else
if ( iLoop == 2 )
{
BackUpSingleServer( sNetDrive,
"Workstatation3",
"CustomerData" );
}
}
}
}
// notice the parameter list in the function immediately below.
// the first three parameters are inputs to this function and
// as such are provided by the calling function.
//
// the next two parameters, which follow a ; character, are
// locally scoped variables
//
BackUpSingleServer( sNetDrive,
sServerName,
sServerShare,
; sTargetCopyPath,
sLastBackUpPath )
{
CNetworkDrive poNetIn = new CNetworkDrive();
sTargetCopyPath = StringCopy( sNetDrive );
sLastBackUpPath = StringCopy( sNetDrive );
// set up the target paths for the backup file
// storage
//
sTargetCopyPath += StringFormat( "\\%.Backup",
sServerName );
sLastBackUpPath += StringFormat( "\\%.Prev.Backup",
sServerName );
if ( poNetIn->Connect( StringFormat( "\\\\%\\%",
sServerName,
sServerShare ),
"Admin",
"Password" ) )
{
sInDrive = poNetIn->GetDriveLetter();
// create a directory object to manage the data
// copy
//
CDirectory poDir = new CDirectory( sInDrive );
poDir->CopyDirectory( sTargetCopyPath,
sLastBackUpPath );
if ( poDir->CompareToOther( sTargetCopyPath ) )
{
// file backup success
}
else
{
Echo( FormatString( "Failure during backup of \\\\%\\%.\r\n",
sServerName, sServerShare ) );
}
poNetIn->Disconnect();
}
else
{
Echo( FormatString( "Failed to backup: \\\\%\\%.\r\n",
sServerName, sServerShare ) );
}
}
The TOOL program above illustrates how to instantiate and make use of TOOL language class objects in a program. This demonstration program also shows how TOOL can be put to multiple uses in an organization in addition to its use as a data-management and reporting system by showing how a simple network backup program can be written in TOOL. TOOL Registry OperationsSince TOOL developers may need to have their scripts interact with the system's registry, TOOL offers APIs for interacting with this aspect of the host system as well. The support offered by TOOL is enough to use the Registry for I/O of strings and numbers. Since nearly all TOOL data types can be converted to these two variable types, the TOOL API is so constrained for simplicity and ease of use (a recurring design goal for TOOL). If additional functionality is required, then it will not be a difficult task to extend TOOL to support any new requirements with regards to operating with the system registry. As is typical for this document, a class wrapper for this section of the TOOL API is found in the ToolBox.Tool file and in the TOOL class called TOOL System Environment OperationsPeriodically, in certain types of system instillations, the operating system's environment variables are used for certain purposes. For operations in these types of installations, TOOL provides functions that allow TOOL scripts to read/write to the system environment variables. The TOOL class called TOOL Process Control FunctionsTOOL offers functions for starting, checking-on, and monitoring other processes. This allows TOOL to inter-operate with already existing programs, and/or to coordinate the activities of the system via the control over processes which are external to the TOOL run-time engine. TOOL can simply spawn a secondary process with the following call: ProcStartNoWait( sProcessCommandLine ); When this API is invoked, the TOOL run-time will forward the process-command-line to the operating system for execution and then will return immediately, so as to not wait for the newly-started process. A related function is to query the operating system to determine if a particular process is running in the operating system where the TOOL runtime is hosted. This call is shown below: IsProcessRunning( sProcessName ); These two functions can allow TOOL to be utilized as a simple-to-use watchdog program (a program that keeps another process running). The following example illustrates this concept: WatchDog( sProcessName, sProcessCmdLine )
{
while ( 1 )
{
if ( !IsProcessRunning( sProcessName ) )
{
// if process is not running, then start it
//
ProcStartNoWait( sProcessCmdLine );
}
//
// since this is an infinite loop, yield control on
// each pass through the loop
//
Sleep( 5000 );
}
}
Another interesting TOOL API for starting external processes is: StartLoggedProcess( sProcssesCmdLine, sFullPathToLogFile ); This API will start up a process and then will capture all data from that process' "stdout" to a log file passed to this function. This means of starting processes is very useful for running simple utility programs from the TOOL system, and then capturing the output from those utilities to a file for future trouble-shooting or analysis. The next TOOL API for starting processes is one that allows a TOOL script to get a process handle from the operating system for the process just started. This handle then can be used as a "wait handle" so that TOOL can wait for the process to complete prior to continuing. hHandle = StartProcessGetHandle( sProcessCmdLine ); .... ReleaseHandle( hHandle ); The ProcStartErrorCheck( sRunLine, oVecErrorList ); The following function illustrates how to use this TOOL API function: StartProgramAndCheckResults( sCommandLine; oVecErrorList, bSuccess )
{
oVecErrorList = Vector();
bSuccess = Byte( 0 );
// add a series of "error strings" to a list of phrases
// the output from the slave process will be examined against the
// error list. If the slave process produces any of these phrases
// the results of the process execution will be reported as an
// error
//
VectorAddItem( oVecErrorList , "ERROR" );
VectorAddItem( oVecErrorList , "UNAVAILABLE" );
VectorAddItem( oVecErrorList , "DOES NOT EXIST" );
VectorAddItem( oVecErrorList , "INCORRECT SYNTAX" );
VectorAddItem( oVecErrorList , "NO SUCH FILE OR DIRECTORY" );
VectorAddItem( oVecErrorList , "COLUMN DOES NOT ALLOW NULLS" );
VectorAddItem( oVecErrorList , "INVALID OBJECT NAME" );
VectorAddItem( oVecErrorList , "INVALID COLUMN NAME" );
VectorAddItem( oVecErrorList , "CHOOSE ANOTHER" );
VectorAddItem( oVecErrorList , " LEVEL" );
VectorAddItem( oVecErrorList , "FAILED" );
VectorAddItem( oVecErrorList , "FILE NOT FOUND" );
// run a slave process and verify its output.
// NOTE: The TOOL script will pause at this point until the
// slave process ends. Utility type programs that exit
// after completion of a task(s) are the most appropriate
// type of process to start with this TOOL API function.
//
bSuccess = ProcStartErrorCheck( sCommandLine, oVecErrorList );
if ( !bSuccess )
{
// since Throw will exit this, make sure to free all
// memory used here
//
VectorClear( oVecErrorList );
FreeObject( oVecErrorList );
Throw( StringFormat( "The program % returned an error.\r\n",
sCommandLine ) );
}
VectorClear( oVecErrorList );
FreeObject( oVecErrorList );
}
TOOL Inter-Process Synchronization PrimitivesTOOL offers a complete set of process-synchronization variable-types. These variable types are important in those installations where TOOL script programs will coordinate actions with other external programs/processes through the signaling of these cross-process variables. These variables will also be used internally in TOOL multi-threaded-scripts. The explanation of the appropriate use of these variable-types is beyond the scope of this document. For that type of information, the reader is referred to any reference that deals with multi-threaded programming techniques. A class wrapper for each of the TOOL synchronization variable types is found in the ToolBox.Tool file. The first variable type you may want to examine is a class wrapper for a semaphore, which as you may recall is an inter-process variable that has a "count" associated with it. The variable can be "acquired" simultaneously by as many processes/threads as the "count" supports. See the The next type of inter-process variable supported in TOOL is the Mutex. This type of variable can only be "acquired" by a single process and/or thread at any time. This type of variable is a "mutual exclusion" gate and serves as a global "single threaded control" across the entire operating system. The TOOL class wrapper for that variable type is called The final type of inter-process variable supported in TOOL is an Event. An event can be used to "send signals" between processes and/or threads for purposes of coordinating activities between them. Properly understood and used, Events can lead to powerful interactions between completely separate processes. The Now that we've covered the creation and simple management of the inter-process variables supported in TOOL, the next topic (naturally) is how to use them in "wait sets" the use of which allows TOOL to coordinate its actions with other external processes. The Working with TOOL Collection ObjectsTOOL offers several data types for managing collections of data. Included in the TOOL installation are sample programs that illustrate how to work with each collection type. It is hoped that the reader will start to become more familiar with class declarations in the TOOL language at the same time that they are being introduced to the collections API functions themselves. Determining Element Counts of any CollectionTOOL offers a
as follows: lElements = SizeOf( oCollectionTypeVar ); TOOL String OperationsStrings are probably the most common data types used in nearly all data processing tasks. Due to that situation, string manipulation is probably the most common types of operations performed in data processing. Because of this, the TOOL API for Strings is the "largest single section" dedicated to a single data type. The complete list of String-related API functions is listed below. Since the String-related API is as rich as it is, the sample class is likely to be the largest sample included in the samples provided with this distribution. iResult = StringCompare( sOne, sTwo ); // returns -1 if sOne < sTwo // 0 if sOne = sTwo // 1 if sOne > sTwo sResult = FillString( sBase, iStartAt, iSpanFor, sFillWith ); iIndex = IndexOf( sBase, sToFind ); sResult = TrimString( sToTrim, sType ); // sType = "l" trim left // = "r" trim right // = "b" trim both iLength = StringLength( sText ); sResult = ToLower( sInput ); sResult = ToUpper( sInput ); sResult = SubString( sInput, iStartAt, iLength ); sResult = StringGetAt( sInput, iIndex ); sResult = StringConcat( sInput, sToAppend ); sResult = StringReplace( sInput, sToReplace, sReplaceWith ); sResult = StringFormat( sFormatString, ... ); sResult = StringBoundedBy( sInput, sLowerToken, sUpperToken ); sResult = BoundedStringReplace( sIn, sLowToken, sUpToken, sNewText ); sResult = TrimQuotes( sInput ); sOutput = StringCopy( sInput ); sResult = PutQuote( sInput ); sResult = PadLeft( sInput, iPadSize ); sResult = PadRight( sInput, iPadSize ); bNumber = IsStringNumeric( sInput ); A rather large TOOL class that implements a class wrapper over the TOOL String-based API functions is the The class also illustrates a few of the string-to-other-data-type conversion functions offered by TOOL. Even so, it is a straight-forward class and as such should offer no difficulties to the reader. Another very important string function offered by TOOL is the " sResult = StringFormat( "Today's data is: %", GetDate() ); sResult = StringFormat( "The value mapped in the under the key % is %", iKey, MapFindByKey( oMap, iKey ) ); With the To 'escape' the '%' character and output an actual percent sign, use '%%' which will output a single percent character into the resultant string at that location. While there is no practical limit to the number of arguments that can be passed to the sString = "The quick brown fox"; sString += "jumped over the lazy dog"; sString += "and the cow \"jumped\" over the moon"; In the example above, you can also see that the '\' character can be used to "escape" a double-quote that should be embedded in the resultant string. Tokenizing Strings in TOOLTo perform simple tokenization on string type variables, TOOL offers a built in tokenizer. A class wrapper for TOOL's String tokenizer functions is found in the TOOL Vector OperationsTOOL offers a set of functions specific to operating on Vector type variables. For all these functions, the first argument to each of these functions is the Vector object upon which the function should operate against. Each of these operations will be introduced below. TOOL Vector objects can contain a "mixed collection" of objects, meaning that each of the elements stored in the collection can be of different types. VectorSetAtIndex( oVector, iIndex, vValue ); VectorGetAtIndex( oVector, iIndex ); VectorAddItem( oVector, vValue ); VectorDelAtIndex( oVector, iIndex ); VectorClear( oVector ); See the In examining the Note: It is considered good form to validate all arguments prior to invoking the API functions if there is any doubt as to the type of variable being passed to the TOOL runtime engine. Adopting this philosophy will help to make your TOOL scripts more robust and reliable and will prevent abrupt halts of your scripts by the run-time should you pass incorrect variable types into the TOOL API functions. Through class design techniques, it is possible to implement TOOL collections that are "type checked" as is shown in the extension to the In the In addition, the TOOL Byte Array OperationsOne of the TOOL data collection variable types is the Byte Array variable type. This type is similar to the Vector type explained in another section of this document, with one very important difference: the Byte Array only stores arrays of bytes. This class is useful as a "buffer class" for forming particular arrangements of byte values. In looking over the list of functions shown below, you can see some of the Byte Array related functions offered by the TOOL API. You can see that the ByteArrayGetAtIndex( oByteArray, iIndex ); ByteArraySetAtIndex( oByteArray, iIndex, bValue ); ByteArrayAppend( oByteArray, bAByte ); ByteArrayAppend( oByteArray, sAString ); ByteArrayAppend( oByteArray, oAnotherByteArray ); ByteArrayInsertAtIndex( oByteArray, iIndex, bAByte ); ByteArrayInsertAtIndex( oByteArray, iIndex, sAString ); ByteArrayInsertAtIndex( oByteArray, iIndex, oAnotherByteArray ); ByteArrayDelAtIndex( oByteArray, iStartDeletAtIndex, iCountToRemove ); ByteArrayClear( oByteArray ); The class TOOL Stack OperationsTOOL offers a Stack object for those scripts that can benefit from such a LIFO collection type. Like the Vector type, the Stack can store various variable types in a single collection. A Stack is rather a simple collection type, so the number of API functions dedicated to Stack is more limited than for other TOOL collection types. See the list below: StackPush( oStack, oToPush ); StackPop( oStack ); StackPeek( oStack ); StackClear( oStack ); Refer again to the ToolBox.Tool file for the TOOL Map OperationsCurrently, TOOL offers only one type of associative container; the Map which allows a simple storage container for keyed-value pairs. The TOOL Map data type constricts the types of variables that can be used as keys into the collection; but like all other TOOL containers, the values stored in the container can be any TOOL value type, and multiple variable types can be stored in a single map. If an illegal key type is attempted to be used with the TOOL Map, the TOOL Run-Time-Engine will halt execution of the TOOL script. Like other TOOL data collections, the Map container is designed to be simple and straight-forward, so the number of Map related functions is limited to those functions required to work with the data type. The list of Map related API calls is listed below: MapRemoveKey( oMap, aKey ); MapFindByKey( oMap, aKey ); MapHasKey( oMap, aKey ); MapClear( oMap ); For an example implementation of a TOOL Map class wrapper, see the TOOL Queue OperationsAnother classic data collection offered by TOOL is the Queue data type. The TOOL Queue is an implementation of the FIFO data collection commonly used in many programming projects. The entire Queue related API is shown below: EnQueue( oQueue, oToPush ); DeQueue( oQueue ); QueueClear( oQueue ); The TOOL class implementation of a Queue class is the TOOL Application Runtime EnvironmentParameters can be passed into the TOOL runtime context for use as "environment variables" for the duration of that TOOL program. TOOL scripts can gain access to these variables with the calls explained below. The RunTool.exe program can be invoked with command-line-arguments that will be forwarded to a TOOL script's runtime context. See the following example for how this could be done: For other TOOL programs that are running in a TOOL run-time environment, there are two additional API calls that can be used to get and set variables within the program context. For fetching a variable from the application server environment, use the API "
depending on the type of the data that was stored in the application context with the call to the API " // store the variables in the application context // SetAppEnvField( "THE_STRING", "Hello" ); SetAppEnvField( "THE_NUMBER", 1234567 ); SetAppEnvField( "FALSE_BOOL", Byte( 0 ) ); SetAppEnvField( "TRUE_BOOL", Byte( 1 ) ); SetAppEnvField( "DOUBLE_VAL", 1234.5678 ); SetAppEnvField( "NEW_YEARS", DateTime() ); // retrieve varialbes from the application context // xFetched = GetAppEnvField( "THE_STRING" ); xFetched = GetAppEnvField( "DOUBLE_VAL" ); See the TOOL class " TOOL Database OperationsSince one of the primary purposes envisioned for TOOL is use in tasks related to data gathering, manipulation and storage of database data, TOOL must offer a database I/O system. The goal of this section of the TOOL API was to provide enough functionality to "get the job done" while attempting to avoid a lot of unnecessary complexity. The interface method used by TOOL to connect to databases is the ODBC drivers offered by all major DBMS vendors. The purpose for this is that this level of interface is the lowest-common-denominator for all database connections (in fact even more 'advanced interfaces' are typically built as additional code layers over ODBC). In addition, it is a ubiquitous technology at this point and therefore will allow TOOL to "connect to virtually anything" on an equal basis. Internally, TOOL has a sophisticated ODBC wrapper technology that allows access to database results to be processed without the TOOL programmer having to deal with the 'grungy details' of dynamically binding to the results of "any conceivable query" that are a part of TOOL jobs. The database functions offered by TOOL are designed for a simple and straight-forward application of database I/O. The "command interface" between TOOL scripts and the connected databases is through SQL-strings passed to the DBMS via the TOOL-managed database connections. Again, the primary design consideration is for ease of use and portability between all database types/vendors rather than to build TOOL too tightly to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||