Getting custom statements to work in Panorama X

On 24 August, Jim set out a basic guide to setting up custom statements in Panorama X. Predictably, it’s not quite as simple as it looks. The following is a first approximation to a definitive guide to doing so. I have to say that it’s a bit of a mine field.

1. Setting up the host database

Create a dedicated database to hold some or all of your custom statements - I’ll refer to it as CustomStats from here on. There are no limitations on the nature of the database name. However, the name of each procedure which you want to be a custom statement may contain only the upper-case letters A to Z, numbers and underscores. The presence of any other character in the name will prevent the procedure from being recognised as a Library procedure. CustomStats may contain other procedures.

Here is an example of code for a simple custom statement called ADDER. I have removed the leading < characters from each tag because the info block was being processed by Discourse and I want to display the original text. The first line is not needed - it’s just my ID system so I know where something came from if I print it down.

/*CustomStats > ADDER 13.9.2016

PROCEDUREINFO>
description>A custom statement which adds two numbers./description>
parameter NAME=FIRSTNUM TYPE=TEXT> The first number. /parameter>
parameter NAME=SECONDNUM TYPE=TEXT> The second number. /parameter>
parameter NAME=TOTALNUM TYPE=TEXT> The sum of the two numbers. /parameter>
/PROCEDUREINFO>*/

FileGlobal fgSumNum
Local fgA1, fgA2

fgA1 = parameter(1)
fgA2 = parameter(2)
fgSumNum = fgA1 + fgA2
return

All of the variables can be Local except those which will return the output from the statement. These must be declared at least as FileGlobal.

2. Registering the procedures in CustomStats

CustomStats must be open and the custom statements must be registered as Library procedures (see below) before any statement is invoked. Assuming that the database serves no other purpose, the best way to do this is to open it as a secret file.

You have several options from this point on. You could open CustomStats every time you boot up Panorama. This is fine until you get a “no such statement” error message that reminds you that you’ve forgotten to do so. Sadly, just opening CustomStats at this stage doesn’t work - the error appears to be persistent. When this happened, I found that I always had to quit Pan X and reboot, opening CustomStats before invoking the statement. This gets very annoying.

My solution (on which I welcome any improvements) is to open CustomStats in the .Initialize procedure of every database which might want to use a custom statement. This is likely to be a fairly small set. It looks like this:

; TestBed .Initialize 13.9.2016

if info(“files”) notcontains "CustomStats"
opensecret "CustomStats"
ScanLibrary "CustomStats"
endif

I’ve included a test for CustomStats already being open because I found that using opensecret on a database that had already been opened in secret caused a whole lot of other problems which I haven’t yet had time to investigate fully.

Another option is to open CustomStats normally and use ZoomWindow or similar to shrink its window and store it in the top left corner of the screen. You will still need to use the ScanLibrary "CustomStats" statement somewhere along the way.

You might think that the ScanLibrary "CustomStats" statement could be in the .Initialize procedure of CustomStats but that doesn’t work - I believe that it has to be in some other database, although it needs to be expressed only once. The assumption thereafter is that, if CustomStats is open, it has been registered.

3. Invoking the custom statement

Here’s a code snippet in which I’ve used the ADDER custom statement:

; TestBed AdderTest 13.9.2016

ADDER 12,36,fgSumNum
message fgSumNum

which gives the result, 48.

There are two interesting aspects of the code. Firstly, the variable, fgSumNum has not been declared. Secondly, its name is the one that was used in the source code in CustomStats. To the best of my knowledge, using any other name will cause an error. How fgSumNum gets into the calling database when it was declared as a FileGlobal is a mystery to me but that’s not important.

4. Next step

If anybody out there has the time to do so, this is fertile ground for research. To date, my procedures appear to be bulletproof but I’ve found that, with cutting-edge stuff like this, Pan X has a habit of springing nasty surprises just when you think you’ve got the job done. I propose to import my suite of custom procedures into Pan X and I’ll no doubt learn some more lessons on the way. I’d really like some extra hands to join in the game - it’s bound to be interesting!

michael

Michael, this post is excellent. Thank you for doing this. Tom

I haven’t tested this, but here is a corrected version of your ADDER routine. Note that if you indent code by 4 spaces, it can be included in Discourse without any messing with < characters.

/*CustomStats > ADDER	13.9.2016

<PROCEDUREINFO>
<description>A custom statement which adds two numbers./description>
<parameter NAME=FIRSTNUM TYPE=NUMERIC> The first number. /parameter>
<parameter NAME=SECONDNUM TYPE=NUMERIC> The second number. /parameter>
<parameter NAME=TOTALNUM TYPE=NUMERIC> The sum of the two numbers. /parameter>
</PROCEDUREINFO>*/

Local fgA1, fgA2, fgSumNum

fgA1 = parameter(1)
fgA2 = parameter(2)
fgSumNum = fgA1 + fgA2
setparameter 3,fgSumNum
return

Firstly, the variable, fgSumNum has not been declared. Secondly, its name is the one that was used in the source code in CustomStats. To the best of my knowledge, using any other name will cause an error. How fgSumNum gets into the calling database when it was declared as a FileGlobal is a mystery to me but that’s not important

I disagree – it is very important. You declared fgSumNum as a fileglobal in your original code for ADDER. So that is going to be declared and used any time you run the custom statement. That’s bad, especially since your custom statement won’t work if you use a different variable name for the third parameter when you call it. My revised version of this code correctly declares this as a local variable and returns it as a parameter, so it works correctly no matter what variable you specify when you call it.

Here’s a further revision that does the whole thing in one line:

setparameter 3,parameter(1)+parameter(2)

Here’s a code snippet in which I’ve used the ADDER custom statement:

ADDER 12,36,fgSumNum

Just to be clear, you do not have to capitalize the statement when you use it. You could also do this:

adder 12,36,fgSumNum

or any other combination of capitalization.

This is sort of true. You could register the database later, but if you do that, you will have to recompile every procedure that uses a statement in your library. Panorama cannot compile a procedure that contains custom statements that haven’t been defined yet.

I’m planning on creating a mechanism that would allow you to place databases in a custom spot in the /Library folder and have them load automatically when Panorama launches, just like Panorama does for it’s own libraries. That will solve a lot of your problems. The fact that I haven’t gotten to that is the reason I was reluctant to document this yet.

Other than these points, thank you Michael for your excellent post.

I’d avoided using the setparameter statement because I had a clear recollection that you’d said it wasn’t allowed in Pan X. What you’d actually said was, “The setparameter statement is not allowed in a procedure called in a function. Instead, the procedure is supposed to use the functionvalue statement to return the value.”

My brain was at a low ebb when I put that piece together and I was fiddling about with back ticks, completely forgetting about the leading spaces trick (which I’ve used frequently in the past. And yes, I know about the capitalisation.

I had planned to put the host databases in a PanoramaX Customs folder in Applications.

Having got this far, I’ll edit the guide and put it back out there so that you’re under no pressure to do it if that’s OK with you

michael