-
Notifications
You must be signed in to change notification settings - Fork 0
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).
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 |
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.
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" |
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: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 customersFBASIC 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" |
| SCNT("collection") | Function returns the total number of elements (count) currently stored in the SDATA collection. | print SCNT("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., SCNT), which contradicts the convention for most other functions in FBASIC. Remember that statements are case-insensitive, but functions are case-sensitive.
- 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 SCNT():
sdata scores 95 ' stores 95 at index 1
sdata scores 88 ' stores 88 at index 2
let total = SCNT(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 90The 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.
| 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"| 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_, _SCNT(), _SCI(), [collection.Item]) do not apply to dynamic collections. Attempting to use them on dynamic collections may result in unpredictable behavior or errors.
This is the most common and robust method for processing all records in the result set.
Foreach DATA1
sdata names \[DATA1.name\]
EndForeach DATA1This 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: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());
....