Skip to content

Language Reference Collections

Andreas AFENTAKIS edited this page Oct 20, 2025 · 15 revisions

Language Reference Collections

In FBASIC, collections are fundamental data structures used to manage lists of values and result sets from data sources. While traditional single variables hold one value, collections allow a program to store and iterate over multiple related items efficiently. The two primary collection types referenced in the language are Static (or SDATA) and Dynamic (Fetchable).

Understand the concept

FBASIC is an extensible computer programming language whose core interpreter provides fundamental structures common to all modern languages, facilitating the easy, statement-by-statement translation to other programming environments. Within FBASIC, data management is distinguished by two primary collection types: static collections, which are typically one-dimensional arrays of data stored directly and contiguously in memory for fast access, and fetchable collections, which are designed for sequential, row-by-row access and do not support direct, indexed lookup of individual rows.

Characteristic Static Dynamic
In memory Yes No
Access row by index Yes No
Access row by fetch Yes Yes
Need reset Yes Yes
Foreach loop Yes Yes
Known number of items Yes No
Multi field support No Yes
Dynamic field naming No Yes
Need data source No Yes
Data from the program (via SDATA) Yes No
Statement SSET Yes No

1. Static Data Collections (SDATA)

An SDATA collection is a dynamic, in-memory structure primarily designed to store and manage lists of primitive values (like strings or numbers) generated or manipulated within the FBASIC program's execution flow. It serves a similar role to an array or list in other languages, allowing for flexible data aggregation.

Creation, Population and Managment

The SDATA collection is created and populated using the SDATA statement.

Statement / Element Purpose Example
SDATA collection value Appends a single value to the end of the collection. sdata names "John"
SCLEAR collection Clear the collections and empty all the contents. The collection is valid but empty of items. prior to clear the statements performs a RESET SCLEAR names
SSET collection index identifier Change the item that the index points to the value or variable SSET names 1 "Jim"

Population Example (from code)

The following example demonstrates initializing an SDATA collection and populating it within a loop:

inputLoop:  
   input nameInput

   If nameInput = "." Then  
      GoTo endInput  
   EndIf

   REM Store the name into the SDATA collection  
   sdata names [nameInput]  
     
   GoTo inputLoop  
endInput:

Retrieval and Iteration

The standard way to access all items in an SDATA collection is using the FOREACH...ENDFOREACH loop structure.

Statement / Element Purpose Example
RESET collection Reset a collection and move the underlyning cursor to the first item RESET names
FOREACH collection Move the underlyning cursor to the next row of a collection. Do not use FETCH inside FOREACH loop FOREACH names ..... ENDFOREACH names
ENDFOREACH collection Trigger to fetch the next row from the collection and reloops ENDFOREACH names
EOD(collection) Functions that inititalized and affected by the CURSOR, FETCH, REST, SCLEAR statements and is true when End Of Data (no more data) occurs if EOD("names") THEN ... ENDIF

Attention:

  • FOREACH...ENDFOREACH loop implicates an automated FETCH on every interation. Do not use FETCH inside such loops.

Example of Iteration:

Foreach customers  
   print [names.FirstName]  
EndForeach customers

Explicit SDATA Manipulation Statements and Functions

FBASIC provides dedicated statements and functions for advanced management of SDATA collections, granting array-like control.

Statement / Element Purpose Example
SSET collection index value Sets/updates the value of an item at a specific, 1-based index in the SDATA collection. SSET names 1 "Jim"
ubound("collection") Function returns the total number of elements (upper bound) currently stored in the SDATA collection. print ubound("names")
SCI("collection", index) Function returns the value of the item at the specified 1-based index in the SDATA collection. print SCI("names",1)
[collection.Item] An identifier with a special syntax used to reference the current fetched item's value. Need FETCH or FOREACH loop before use it. [names.Item]

Special attention:

  • Function names for collections use capital (uppercase) letters (e.g., SCI), which contradicts the convention for most other functions in FBASIC. Remember that statements are case-insensitive, but functions are case-sensitive. Exception is the ubound() that it is buildin basic function common for arrays, collections etc.
  • The collection name passed to these functions is treated as a string value or variable, not an identifier. Therefore, a literal collection name must be enclosed in quotes.
  • In FBASIC most of the of statements have the arguments as space separted, and functions have them as comma separated. This happend to avoid parsing waste time

Example using SSET and ubound():

sdata scores 95  ' stores 95 at index 1  
sdata scores 88  ' stores 88 at index 2

let total = ubound(scores)  
print "Total items: " + total  ' Prints 2

SSET scores 2 90  ' Change value at index 2 from 88 to 90  
let newScore = SCI(scores, 2)  
print "New score at index 2: " + newScore ' Prints 90

2. Dynamic Data Collections (Dynamic or Fetchable Data)

The core of FBASIC interpreter supports the functionallity of Dynamic and Fetchable Data, but does not offer any way to use them because does not have builtin functionality to access any kind of such data. To gain access to your data via a dynamic collection, you need to implement a Data Provider and a Library that will give programming statements and functions to the FBASIC interpreter.

With the package as addons are avaiable the following FBASIC Libraries:

  • FBasicSQLDataAdapter, where provide access to fetchable SQL Statements, is a special type of collection that acts as a wrapper for external data sources, most of the cases SQL results. It is designed for efficient, record-by-record retrieval of data without loading the entire dataset into memory at once.

Each of the libraries offering Dynamic Data Collection functionallity, implements its own statements to declare the fetchable data. The buildin FBasicSQLDataAdapter is using the statement CURSOR where it is included into current documentation.

2.1. Library: FBasicSQLDataAdapter

Creation

Statement / Element Purpose Example
CURSOR collection, sql_statment Declare a SQL cursor for a data retrival command (eg SELECT) cursor DATA1, "select top 20 id,name from friends order by name"

Attention: In contradiction with most of the FBASIC's statements, CURSOR syntax needs comma (,) after the collection name.

Example:

Cursor c1, "select top 20 id,name where type='S' from sysobjects order by name"

Retrieval and Navigation Statements and Functions

Statement / Element Purpose Example
RESET collection Reset a collection and move the underlyning cursor to the first item RESET names
FOREACH collection Move the underlyning cursor to the next row of a collection. Do not use FETCH inside FOREACH loop FOREACH names ..... ENDFOREACH names
ENDFOREACH collection Trigger to fetch the next row from the collection and reloops ENDFOREACH names
EOD(collection) Functions that inititalized and affected by the CURSOR, FETCH, REST, SCLEAR statements and is true when End Of Data (no more data) occurs if EOD("names") THEN ... ENDIF
sql(v) Surrounds the input value with quotes if the input variable is string sql(1) or sql(var1) or sql("john")
[collection.field_name] Field reference getting the value of a field from the currently fetched record. print "[c1.id], [c1.name]"

Note: Functions and statements designed for static collections (e.g., _SSET_, _ubound(), _SCI(), [collection.Item]) do not apply to dynamic collections. Attempting to use them on dynamic collections may result in unpredictable behavior or errors.


3. More examples

Example, Standard FOREACH Loop

This is the most common and robust method for processing all records in the result set.

Foreach DATA1  
   sdata names \[DATA1.name\]  
EndForeach DATA1

Example, Manual FETCH Loop

This method offers fine-grained control over reading records, often used with conditional branching using EOD().

dataloop1:  
fetch DATA1  
If EOD("DATA1") Then  
   GoTo exitloop  
EndIf

let msg= "ID: " \+ sql(\[DATA1.id\]) \+ ", Name: " \+ sql(\[DATA1.name\])  
print msg

GoTo dataloop1

exitloop:

Example,Static Collection

Using the statement SDATA the developer can define a static collection of data. For further information see the following example:

REM SDATA Test
REM 
sdata Months "Jan","Feb","Mar"
sdata Months "Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
sdata DayNo 1,2,3,4,5,6,7
let ln=1
Foreach Months
let msg=str(ln)+". "+[Months.Item]
print msg
let ln=ln+1
EndForeach Months
assert ln = 13

print "Second list:"
reset Months
Foreach Months
print [Months.Item]
EndForeach Months

print "Day Numbers:"
Foreach DayNo
print [DayNo.Item]
EndForeach DayNo

Example, demonstration of CURSOR, nested ForEeach loops, and RESET statement

  REM Data count loop
  Cursor DATA1, "select top 2 name from sysobjects order by name desc"
  Cursor DATA2, "select top 5 id,name from sysobjects order by name"
  let row=0
  let tms=0
  REM loop
  ForEach DATA1
  ForEach DATA2
    let row=row+1
    let msg= str(row)+": "+sql([DATA1.name])+", "+[DATA2.name]
    print msg
  EndForEach DATA2
  reset DATA2 'Reset it is necessary only in the case that the flow will pass again from the FOREACH statement. 
  EndForEach DATA1

Example, demonstrating the elements: CURSOR, FETCH, EOD(), RESET, GOTO, Labels

SQL Data provider is designed to provide data collections based on a SQL statement and handle it as a read only cursor. To active it add the library:FBasicSQLDataAdapter. Adding the following extensions to the language:

  REM Data print and count loop, two times
  REM
  Cursor DATA1, "select top 20 id,name from sysobjects order by name"
  let row=0
  let tms=0
  REM loop
  dataloop1:
    fetch DATA1
    If EOD("DATA1") Then
       GoTo exitloop
    End If
  
    let row=row+1
    let msg= str(row)+": "+sql([DATA1.id])+", "+sql([DATA1.name])
    print msg
  
    GoTo dataloop1
  
  exitloop:
    REM end loop
    print "***EOD****"
    let tms=tms+1
    If (tms = 1) Then
       reset DATA1
       GoTo dataloop1
    EndIf

4. Connection to the Database

Library FBasicSQLDataAdapter does not have any mechanism to specify the connection to the database, but is using the Request For Object Handler to request an initialized database connection object. So, the delgation for the requestForObjectHandler of the interpreter has to be specified before program execution.

Durring the program execution, when the data adapter needs the database connection object, use a request with the following parameters:

Context Group Name
Values: SQL CONNECTION cursor_name
Example: SQL CONNECTION DATA1

Example of Interpreter Initialization

            this.env = new();
            env.DefaultEnvironment();
...
            env.requestForObjectHandler = (context, group, name) =>
            {
                if ($"{context}.{group}.{name}" == "SQL.CONNECTION.DATA1") 
                {
                    string cs = "<replace with your CS hear>";
                    var connection = new OdbcConnection(cs);
                    return connection;
                }
                if ($"{context}.{group}.{name}" == "IN.TEST.NAME") return "THIS IS AN IN TEST!";
                return null;
            };
            env.AddLibrary(new FBasicSQLDataAdapter());
...

.

Clone this wiki locally