An interesting query made its way into the office this week. Our client wanted to be able to validate data using Regular Expressions (REGEXP) for Email validation (^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$ if you must know) and so unsurprisingly searched the documentation for REGEXP and came across its usage in RTI_JSON. This led to a series of mutual misunderstandings which led to a far greater understanding of what JSON actually is (see footnote) but still didn't help in actually validating using a REGEXP.
Fortunately, we have resources in the office who were able to advise that what we wanted to achieve was actually relatively simple using the OI ActiveX Scripting Host. This is something that is not yet "officially" documented in the product - but like quite a few things in OI, there is profuse documentation in the Equates. In version 10 this has been handily encapsulated for us the the RTI_AXSH function - the ActiveX Scripting Host. ActiveX Scripting and Active Scripting are the same thing. The X was dropped over the years.
But first - what actually is a Scripting Host?
An Active Scripting Host is a technology used in Windows to support component-based scripting. It allows different scripting engines to be integrated into applications. This enables the execution of scripts written in various languages like VBScript, JScript (Microsoft’s implementation of JavaScript), Python, Perl and others. If we have a task we wish to execute that is better suited to say, JScript - performing a REGEXP for example - we can use RTI_AXSH to do this for us.
To show how easy it is, we'll first provide a small sample program to do this, then explain the program and finally document the routine.
The Code
compile function test_revaxsh_regexp( void )
$insert rti_AXSH_Equates
$insert rti_SSP_Equates
$insert logical
call set_Status( FALSE$ )
errStat = SETSTAT_OK$
errText = ""
hAXSH = 0
retVal = ""
code = 're = new RegExp( "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" );'
code := 'function testRE( str ) {'
code := ' return re.test( str );'
code := '}'
createParams = ""
createParams<REVAXSH_CREATE_POS_LANGUAGE$> = "JScript"
hAXSH = rti_AXSH( REVAXSH_MTD_CREATE$, createParams )
errStat = get_Status( errText )
if errStat then
return ""
end
call rti_AXSH( REVAXSH_MTD_ADDCODE$, hAXSH, code )
errStat = get_Status( errText )
if errStat then
return ""
end
string = "amcauley@sprezzatura"
if get_Status() else
retVal = rti_AXSH( REVAXSH_MTD_RUNEX$, hAXSH, 'testRE', string )
errStat = get_Status( errText )
end
if ( hAXSH ) then
call rti_AXSH( REVAXSH_MTD_DESTROY$, hAXSH )
end
if errStat then
debug
End
return retVal
The Explanation
The first thing that we need to do is insert RTI_AXSH_Equates. Once this has been done, we construct some simple JScript code in a variable called code. For those not fluent in JScript it's probably worth explaining how the code operates.
We start by create a global variable called "re". Think of it as being like labelled common. It'll be there until the scripting host is destroyed. Loading the code instantiates the re object as a REGEXP type.
We then define a JScript function called testRE so that we can call it from within Basic+.
We initialise our create parameter to tell OI which scripting language to use and then use the CREATE method to initialise our Script Hosting. Now that we have a container to put our code in, we can add it using the ADDCODE method. Assuming no errors we then use the RUNEX method, telling it the name of the function in our code we wish to execute (testRE). We now have the result we want to do with as we will.
Finally in an act of polite housekeeping we tell the system to DESTROY the scripting host we created so as not to leak resources.
We hope that this information makes the Scripting Host a little less scary!
The Documentation - taken from RTI_AXHS_EQUATES.
The RTI_AXSH routine provides a set of methods for interacting with the ActiveX Scripting Host. Below is a detailed guide on how to use these methods, including their parameters, return values, and error handling.
The following methods are supported
Method | Description |
---|---|
CREATE | Creates an ActiveX scripting host instance and returns its handle. |
DESTROY | Destroys a script host instance. |
GETPROP | Returns the value of a specified property. |
SETPROP | Sets the value of a specified property. |
ADDCODE | Adds a block of code to the scripting host. |
EVAL | Evaluates a statement and returns the result. |
EXECUTE | Executes one or more statements (no result is returned). |
RUN | Executes a specific script function and returns the result (if any). |
GETENGINES | Returns a list of ActiveX script engines installed on the workstation. |
ADDOBJECT | Adds a global named object to the scripting host. |
RUNEX | Executes a specific script function and returns the result (if any), allowing the script function parameters to be passed as separate arguments. |
CREATE
Creates an ActiveX scripting host instance and returns it's handle.
The instance is initialised with the following information:
<1> Script Language (required)
<2> AllowUI (1/0)
<3> hwndSite
<4> Timeout in milliseconds (-1 == no Timeout, otherwise > 0)
e.g.
createParam = "JScript" : @fm : TRUE$ : @fm : @@window->handle
hAXSH = rti_AXSH( REVAXSH_MTD_CREATE$, createParam )
Input | createParam : an @fm delimited dynamic array as specified above |
Returns | The handle of the new AXSH instance of successful, otherwise "0". |
Errors | All errors are returned via Set_Status() |
DESTROY
Destroys a script host instance.
call rti_AXSH( REVAXSH_MTD_DESTROY$, hAXSH )
Input | hAXSH : Handle of the scripting host to destroy [required] |
Errors | All errors are returned via Set_Status() |
GETPROP
Returns the value of a specified property
e.g.
propName = REVAXSH_PROP_LANGUAGE$
language = rti_AXSH( REVAXSH_MTD_GETPROP$, hAXSH, propName )
Input | hAXSH : Handle of the scripting host to access [required] |
Input | propName : Name of the property to access [required] |
Returns | The property value |
Errors | All errors are returned via Set_Status() |
SETPROP
Input | hAXSH : Handle of the scripting host to access [required] |
Input | propName : Name of the property to access [required] |
Input | propValue : New property value to set. |
Errors | All errors are returned via Set_Status() |
ADDCODE
Input | hAXSH : Handle of the scripting host to access [required] |
Input | scriptCode : Code to add. [required] |
Errors | All errors are returned via Set_Status() |
EVAL
Input | hAXSH : Handle of the scripting host to access [required] |
Input | statement : Code to evaluate. [required] |
Returns | The result of the evaluation. |
Errors | All errors are returned via Set_Status() |
EXECUTE
Input | hAXSH : Handle of the scripting host to access [required] |
Input | statement : Code to execute. [required] |
Errors | All errors are returned via Set_Status() |
RUN
Returns | Return value from the function (if any) |
Errors | All errors are returned via Set_Status() |
GETENGINES
Returns | An @fm delimited list of installed engines. |
Errors | All errors are returned via Set_Status() |
ADDOBJECT
Input | hAXSH : Handle of the scripting host to access [required] |
Input | objName : "Script name" of the object being added [required] |
Input | oleVar : OLE object being added [required] |
Errors | All errors are returned via Set_Status() |
RUNEX
Executes a specific script function and returns the result (if any), allowing the script function parameters to be passed as separate arguments (This method is the only method that can pass OLE objects to a script function)
The maximum number of parameters that can be passed is 10. Note that this method stops looking for script function arguments when it encounters the first unassigned one.
e.g.
scriptCode = "function add( a, b ) { return a + b; }"
call rti_AXSH( REVAXSH_MTD_ADDCODE$, hAXSH, scriptCode )
method = "add"
arg1 = 3
arg2 = 4
retVal = rti_AXSH( REVAXSH_MTD_RUN$, hAXSH, method, arg1, arg2 )
Input | hAXSH : Handle of the scripting host to access [required] |
Input | method : name of the function to execute [required] |
Input | arg1 : First parameter to pass to the function |
Input | arg2 : Second parameter to pass to the function |
Input | arg3 : Third parameter to pass to the function |
Input | arg4 : Fourth parameter to pass to the function |
Input | arg5 : Fifth parameter to pass to the function |
Input | arg6 : Sixth parameter to pass to the function |
Input | arg7 : Seventh parameter to pass to the function |
Input | arg8 : Eighth parameter to pass to the function |
Input | arg9 : Ninth parameter to pass to the function |
Input | arg10 : Tenth parameter to pass to the function |
Errors | All errors are returned via Set_Status() |
A note relating to all scripting errors
The Execute, AddCode, Eval and Run methods can all return parsing and execution errors from the scripting engine. In this case an @svm delimited list of error information is returned with the following structure:
<0,0,1> Error Source
<0,0,2> Error Description
<0,0,3> LineText
<0,0,4> LineNumber
<0,0,5> CharPos
<0,0,6> ContextID
Footnote:
Pro Tip
CoPilot understands RegExp well. It suggests replacing the REGEXP we started with with "^[\w.%+-]+@[\w.-]+\\.\w{2,}$".
No comments:
Post a Comment