Category Archives: Basic+

Object Notation Syntax

The OpenInsight event compiler supports an enhanced “shorthand” syntax for working with the Presentation Server object model, much like that provided in standard Basic+ for use with OLE objects.  Like the OLE notation, this provides a more natural API to working with properties and methods, rather than the relatively verbose and flat interface provided by the familiar Get/Set_Property and Exec_Method functions.

In a nutshell, object notation provides the use of a special “->” operator to allow an object to refer to its properties and methods, along with the “{}” operators to specify object or property indexes. It may be used in place of the following function calls:

  • Get_Property
  • Set_Property_Only
  • Exec_Method

Using Properties

The general format for accessing properties via object notation is illustrated below. In all cases objects that support sub-objects (such as controls that support an IMAGE sub-object) may reference the sub-object by suffixing them to the main object with a “.” character as a delimiter.

Get_Property syntax

  value = object->property                        ; // Non-Indexed
  value = object{index}->property                 ; // Object-Indexed
  value = object->property{index}                 ; // Property-Indexed

  // With sub-object support
  value = object.subObject->property              ; // Non-Indexed
  value = object.subObject{index}->property       ; // Object-Indexed
  value = object.subObject->property{index}       ; // Property-Indexed

Set_Property_Only syntax

  object->property = value                        ; // Non-Indexed
  object{index}->property = value                 ; // Object-Indexed
  object->property{index} = value                 ; // Property-Indexed

  // With sub-object support
  object.subObject->property = value              ; // Non-Indexed
  object.subObject{index}->property = value       ; // Object-Indexed
  object.subObject->property{index} = value       ; // Property-Indexed

Where:

  • object is either:
    1. An equated constant (suffixed with a “$” symbol), or
    2. The contents of a variable (prefixed with an “@” symbol), or
    3. An embedded name (prefixed with the “$” symbol), or
    4. A path prefix (prefixed with the “.” symbol to represent the name of the object’s parent window (i.e. “@Window”)
  • property can be an equated constant, the contents of a variable, or an embedded name. It may also be the special token “@@” which means use the DEFPROP property.
  • index is either a one or two dimensional index value, delimited by a “,” character and surrounded by curly braces.

Get_Property examples

 // Get_Property object notation using variable contents
 CtrlID = @Window : ".MY_LISTBOX"
 
 // PropVal = Get_Property( CtrlID, "TEXT" )
 PropVal = @CtrlID->Text
 
 // PropVal = Get_Property( CtrlID, "LIST", 4 )
 PropVal = @CtrlID->List{4}
 
 // PropVal = Get_Property( CtrlID, "LIST", ItemIdx )
 ItemIdx = Get_Some_Index()
 PropVal = @CtrlID->List{ItemIdx}
 
 // PropVal = Get_Property( CtrlID, "DEFPROP" )
 PropVal = @CtrlID->@@
 
 // PropVal = Get_Property( @Window, "TEXT" )
 PropVal = @@Window->Text
 
 EdtID   = @Window : ".MY_EDITTABLE"; Col = 2; Row = 3
 
 // PropVal = Get_Property( EdtID : ".CELLS", "TEXT", Col : @fm : Row )
 PropVal = @EdtID.Cells{Col,Row}->Text
 // Get_Property object notation using a path-prefix 
 
 // PropVal = Get_Property( @Window : ".MY_LISTBOX", "TEXT" )
 PropVal = .My_ListBox->Text
 
 // PropVal = Get_Property( @Window : ".MY_LISTBOX", "LIST", 4 )
 PropVal = .My_ListBox->List{4}
 
 // PropVal = Get_Property( @Window : ".MY_EDITTABLE.CELLS", "TEXT", 2 : @fm : 3 )
 PropVal = .My_EditTable.Cells{2,3}->Text
 // Get_Property object notation using equated constants  
 Equ CTRLID$ To "MYWIN.MY_LISTBOX"
 
 // PropVal = Get_Property( CTRLID$, "TEXT" )
 PropVal = CTRLID$->Text
 
 // PropVal = Get_Property( CTRLID$, "LIST", 4 ) 
 PropVal = CTRLID$->List{4}
 
 Equ EDTID$ To "MYWIN.MY_EDITTABLE"
 
 // PropVal = Get_Property( EDTID$ : ".CELLS", "TEXT", 2 : @fm : 3 )
 PropVal = EDTID$.Cells{2,3}->Text
 // Get_Property object notation using an embedded name
 
 // FocusID = Get_Property( "SYSTEM", "FOCUS" )
 FocusID = $System->Focus 

 // PropVal = Get_Property( "MYWIN.MY_CONTROL", "TEXT" )
 PropVal = $MyWin.My_Control->Text

Set_Property_Only examples

 // Set_Property_Only object notation using variable contents
 CtrlID = @Window : ".MY_LISTBOX"
 
 // Call Set_Property_Only( CtrlID, "TEXT", PropVal )
 @CtrlID->Text = PropVal
 
 // Call Set_Property_Only( CtrlID, "LIST", PropVal, 4 )
 @CtrlID->List{4} = PropVal
 
 // Call Set_Property_Only( CtrlID, "DEFPROP", PropVal )
 @CtrlID->@@ = PropVal
 
 // Call Set_Property_Only( @Window, "TEXT", PropVal )
 @@Window->Text = PropVal
 
 EdtID = @Window : ".MY_EDITTABLE"; Col = 2; Row = 3
 
 // Call Set_Property_Only( EdtID : ".CELLS", "TEXT", PropVal, Col : @fm : Row )
 @EdtID.Cells{Col,Row}->Text  = PropVal
 // Set_Property_Only object notation using an embedded name
 
 // Call Set_Property_Only( "SYSTEM", "FOCUS", focusID )
 $System->Focus = FocusID
 
 // Call Set_Property_Only( "MYWIN.MY_CONTROL", "TEXT", PropVal )
 $MyWin.My_Control->Text = PropVal
 // Set_Property_Only object notation using a path-prefix 
 
 // Call Set_Property_Only( @Window : ".MY_LISTBOX", "TEXT", PropVal )
 .My_ListBox->Text  = PropVal
 
 // Call Set_Property_Only( @Window : ".MY_LISTBOX", "LIST", PropVal, 4 )
 .My_ListBox->List{4} = PropVal
 
 // Call Set_Property_Only( @Window : ".MY_EDITTABLE.CELLS", "TEXT", PropVal, |
 //                         2 : @fm : 3 )
 .My_EditTable.Cells{2,3}->Text = PropVal
 // Set_Property_Only object notation using equated constants
 Equ CTRLID$ To "MYWIN.MY_LISTBOX"
 
 // Call Set_Property_Only( CTRLID$, "TEXT", PropVal )
 CTRLID$->Text  = PropVal
 
 // Call Set_Property_Only( CTRLID$, "LIST", PropVal, 4 ) 
 CTRLID$->List{4} = PropVal
 
 Equ EDTID$ To "MYWIN.MY_EDITTABLE"
 
 // Set_Property_Only( EDTID$ : ".CELLS", "TEXT", PropVal, 2 : @fm : 3 )
 EDTID$.Cells{2,3}->Text = PropVal

Using Methods

The general format of the Exec_Method object notation is described below.  It may be used to execute the method as a subroutine (i.e. no return value) or as a function.

Exec_Method syntax

  object->method( arg1, arg2, … argN )            ; // Call as subroutine
  result = object->method( arg1, arg2, … argN )   ; // Call as function

Where:

  • object is either:
    1. An equated constant (suffixed with a “$” symbol), or
    2. The contents of a variable (prefixed with an “@” symbol), or
    3. An embedded name (prefixed with the “$” symbol), or
    4. A path prefix (prefixed with the “.” symbol to represent the name of the object’s parent window (i.e. “@Window”)
  • method can be an equated constant, the contents of a variable, or an embedded name.

Exec_Method examples

 // Exec_Method object notation using variable contents
 CtrlID = @Window : ".MY_LISTBOX"
 
 // Pos = Exec_Method( CtrlID, "INSERT", -1, Item )
 Pos = @CtrlID->Insert( -1, Item )
 
 // Call Exec_Method( CtrlID, "DELETE", 4 )
 @CtrlID->Delete( 4 )
 // Exec_Method object notation using a path-prefix 
 
 // Pos = Exec_Method( @Window : ".MY_LISTBOX", "INSERT", -1, Item )
 Pos = .My_ListBox->Insert( -1, Item )
 
 // Call Exec_Method( @Window : ".MY_LISTBOX", "DELETE", 4 )
 .My_ListBox->Delete( 4 )

 // Call Exec_Method( @Window : ".MY_EDITTABLE", "APPEND", RowData )
 .My_EditTable.Rows->Append( RowData )
 // Exec_Method object notation using equated constants
 Equ CTRLID$ To "MYWIN.MY_LISTBOX"
 
 // Pos = Exec_Method( CTRLID$, "INSERT", -1, Item )
 Pos = CTRLID$->Insert( -1, Item )
 
 // Call Exec_Method( CTRLID$, "DELETE", 4 ) 
 CTRLID$->Delete( 4 )
 // Exec_Method object notation using an embedded name
 
 // RetVal = ( "SYSTEM", "CREATE", createStruct )
 RetVal = $System->Create( createStruct )
 
 // Call Exec_Method( "SYSTEM", "DESTROY", ctrlID )
 $System->Destroy( ctrlID )

Using Object Notation in Stored Procedures

Object Notation was originally designed for use with the event compiler, and therefore prior to version 10 could only be used with event scripts.  In the current version however, it may be used in Stored Procedures by including the event pre-compiler in the compilation chain.  This is done by adding the following at the top of the program before the other statements:

  #Pragma PreComp Event_PreComp

You should also declare the following functions before you use any object notation – the pre-compiler does not insert these into the program itself:

  • Get_Property
  • Exec_Method

E.g.

 Compile Function MyWin_Events( CtrlEntID, Event, Param1, Param2 )
 
   #Pragma PreComp Event_PreComp
   
   Declare Function Get_Property, Exec_Method
   $Insert Logical
   
   Locate Event In "CREATE,CLICK,CLOSE" Using "," Setting Pos Then
      On Pos GoSub OnCreate,OnClick,OnClose
   End
   
 Return RetVal 

   // ... etc ... 

Unlike in previous versions this object notation may also be used safely with OLE object notation in the same Stored Procedure.

Object Notation limitations

The current version of Object Notation is currently handled by a pre-compiler, rather than the actual Basic+ compiler itself, thus its parsing accuracy is somewhat limited in comparison.  Because of this, the following guidelines should be adhered to:

  1. The passing of complex expressions to the object notation Set_Property_Only and Exec_Method statements should be avoided; It is better to resolve them to a variable first, and then pass that variable as an argument instead.
  2. The curly-brace Calculate operators (“{” and “}”) are also used to resolve the value of a dictionary column at runtime, and should not be used on the same line as an object notation statement: These operators are interpreted as object or property index tokens instead, and will lead to parsing errors if used incorrectly.

Object Notation troubleshooting

Behind the scenes the pre-compiler converts the object notation syntax to actual Get_Property, Set_Property_Only and Exec_Method calls before passing them to the Basic+ compiler.  If you use object notation and run into problems that you cannot resolve easily you can see exactly what gets passed to the compiler by using the Output compiler directive, which will write the pre-compiler output to a specified record.

To enable this functionality, place the following statement at the top of your program (before or after the Event_PreComp statement), and replace <table> and <record> with the table and record names of your choice:

   #Pragma Output <table> <record>

E.g. Send the output to the PRECMP_OUT record in SYSLISTS

   #Pragma Output SYSLISTS PRECMP_OUT

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).

Insert records and the TCOMPILE method

One style of programming we generally try to avoid is placing executable code statements (*) in a “$insert” record (i.e. an STPROCINS entity) and using that in another program because it usually suffers from the following problems:

  • Inserted code cannot be traced properly with the debugger, making it hard to step through a procedure and possibly obscuring variable initialization and manipulation.
  • Changes to the inserted code will force all the hosting stored procedures to need recompilation.
  • The BLint() process that the compiler uses to check for suspected unassigned variables will not follow the $insert statement and process the insert record, making compile time errors more difficult to detect.

However, whilst working on the new Form Designer we found there was quite a bit of common code that was shared between the various modules used by the form parser and compiler, and we obviously didn’t want to duplicate this in each one.  Normally we would just move the code into a separate stored procedure and call that from each module, but that just adds extra overhead in a process that we really wanted to keep as fast as possible, and so we looked at another option: moving it into an insert record instead. Sharing the code this way means that it runs “inline”, thereby removing the need to create another call-frame, move variables on and off the stack, and so on.

The shared code involved was fairly straightforward, well-tested, and limited to a few small subroutines, so the only real issue to overcome was: how can we easily update any “host” stored procedures in the event of the insert record being changed?

Well, one of the nice things about the OpenInsight repository is that it tracks the various relationships between entities in an application, and some types support a method called TCOMPILE (Tree-Compile) that allows an entity to compile any other entities they are using during the compilation process as well.  However, in this case we needed to do the opposite and compile those entities that were using the insert record instead, and so we created a TCOMPILE method for the STPROCINS type that processes this list of “used-by” entities and executes their normal COMPILE method after the insert record has been saved. This results in a simple way to ensure that changes are implemented across any affected programs.

Whilst we wouldn’t say we exactly endorse embedding programs in this way, it does remain a useful technique for sharing code in some limited cases, and with the new TCOMPILE method it is now much easier to manage too.

(* e.g. code statements such as “x = y + z; call msg( @window, txt );” etc, as opposed to simple constant declarations like equate statements).

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).