firstobject Access Language
The firstobject access language "FOAL" based on C++ syntax is specialized for retrieving and reporting information in markup text documents. It also introduces an efficient and versatile way of doing lists and structures using markup. Built around the CMarkup API, it opens up new potential for rapid development of high performance processing and transformation of XML and other markup documents.
Unit
A document or file is a "unit" that contains functions. If there are multiple functions, the unit will often have a primary function for the sake of running it conveniently. You can name the primary function main(), or it will choose the last function that has no parameters.
Differences from C++
FOAL is based on C++ syntax, just like Java is. This is not to be confused with being C++. FOAL supports a small subset of C++ syntax and capabilities as well as some of its own unique flavors. The C++ syntax was chosen to be compatible with the examples already supplied in the firstobject documentation.
The main differences from C++ are the fundamental types str and num and the automatic conversion of types with operators or when they are passed to functions.
FOAL has no pointers. Functions can be declared to accept arguments "by reference" meaning that operations on the variable inside the function will affect the variable that was passed in. Plus FOAL allows default values for reference arguments, unlike C++.
Also, for a function that does not return a value, the void keyword in front of the function declaration is optional (and you cannot use void in the parameter list).
FOAL currently only supports while loops. Any loop can be written as a while loop, so this keeps the syntax simple. People not accustomed to C/C++ languages may be unfamiliar with the parts of a for loop or the syntax of a do loop, so it is debatable whether these should be supported in FOAL.
Types
FOAL has the following types:
int a 32-bit integerbool a boolean value, false=0, true=1num a precision decimal numberstr a Unicode stringCMarkup a markup documentFOAL provides automatic conversion between types for assignments and passing to arguments as follows:
| Converted To: | |||||
|---|---|---|---|---|---|
bool |
int |
num |
str |
CMarkup |
|
bool |
0 or 1 |
0 or 1 |
"0" or "1" |
error | |
int |
true if != 0 |
exact | minus, digits | error | |
num |
true if != 0 |
lose fraction | "." separator | error | |
str |
error | minus, digits | "." separator | SetDoc |
|
CMarkup |
error | error | error | GetDoc |
|
When going between numbers and strings, a period is always used to represent the decimal point "separator". This ensures that programs work the same way regardless of locale. It is also good practice to standardize on a period in XML documents that may pass between locales, and just use the implicit conversion that expects periods. When dealing with locale dependent display or data entry, use the NumToFormat function to create a locale-specific number string for displaying, and NumFromFormat to convert from an input string.
Strings
Most examples in the CMarkup documentation can be run in the firstobject access language, with only minor changes relating to the string type. Since CMarkup is developed primarily to work with both MFC (CString) and STL (std::string) strings, simple modifications are necessary when using an example written for one or the other. This means changing the string type name and converting any string functions to the FOAL string functions such as StrLength, StrCompare, StrFind, StrMid.
The str type is Unicode, and you cannot assume an correspondence between length and number of characters unless you know your text is ASCII.
Numbers
An int is like a num where the number of decimal places is always zero.
The num type supports decimals like 9.95. It is not implemented as a C-style floating point number, but instead as an integer with a "floating" number of decimal places. In other words, 9.95 is stored as 995, and number of decimal places of 2. This allows FOAL to support more accurate arithmetic than C-style floating point number types (float and double) which have inherent inaccuracies (see Precision and Accuracy in Floating-Point Calculations).
Because num retains a number of decimal places, specifying 9.50 is the same value as 9.5 but you are telling it to maintain two decimal places rather than one. FOAL will keep the number of decimal places as additions and subtractions are made.
9.50 + .5 = 10.00 9.50 - 9.5 = 0.00
With division and multiplication, the number of decimal places may grow if required to up to the maximum precision supported by the type. With multiplication it will never be more than the total of the decimal places in the operands. With division it can often go to the limits of precision and you should probably round the result to the desired precision.
9.50 * 2.0 = 19.00 .3 * .5 = 1.5 2.01 * 0.3 = 0.603 .3 / 2 = 0.15 .30 / 2 = 0.15 20 / 3 = 6.66666666
You can confidently compare num values, and int values too, regardless of the number of decimal places:
1.5000 == 1.5 1 == 1.0
Also, there are no floating-point concerns with rounding, it always yields the expected results:
NumRound( 9.5 ) == 10 NumRound( 9.85, 1 ) == 9.9 NumRound( 9.95, 1 ) == 10.0 NumRound( 155, -1 ) == 160
Lists and Arrays
Here is some code to create a list of values:
CMarkup mList; mList.AddElem( "E", "Smith" ); mList.AddElem( "E", "Doe" ); mList.AddElem( "E", "Jones" );
<E>Smith</E>
<E>Doe</E>
<E>Jones</E>
You can loop through all the items in the list with FindElem. To iterate in reverse order starting at the last sibling just replace FindElem with FindPrevElem in the following example.
mList.ResetPos();
while ( mList.FindElem() )
{
str sLastName = mList.GetData();
// ...
}
Although it is more efficently used as a list where you loop through the elements sequentially, you can also access it like an array. To go directly to the second item and get the value call:
str sVal = mList.FindGetData("/*[2]");
Note the slash in /*[2] which means absolute path so that it does not matter where the current position is when you call this. Using the asterisk instead of the tag name E means that the tag name does not matter.
Structures
There are no structures or classes like C++, but you can store any complex set of data in a CMarkup object (see Dynamic Structure Documents). The subdocument functions (AddSubDoc, GetSubDoc) can help mimic substructures as well (see Subdocuments and Fragments of XML Documents).
Good point. I intend to implement this because it is an assumption people make when using C++ syntax that the second part of a conditional expression will not get called or evaluated when the first part makes evaluating the second part unecessary. If the first argument of a logical OR evaluates to true or if the first argument of a logical AND evaluates to false, then it doesn't need to evaluate the second argument. This makes the code for the short circuit operators (|| and &&) act like a control structure. In the following example, the expectation would be that FindElem(sName) will only be called if sName is not an empty string:
if ( sName != "" && m.FindElem(sName) ) ...
Although the firstobject XML editor FOAL evaluates this conditional statement correctly, it does not currently support the expected "short circuit" when sName is empty. It will call the FindElem method and possibly change the current position within the CMarkup object even if sName is empty. For now, you have to work around it with something like this:
if ( sName != "" )
{
if ( m.FindElem(sName) )
...
}
I will update here when the expected usage is implemented.


Joe McKeown 01-May-2009
Something I noticed (the hard way) is that FOAL doesn't seem to support 'short circuit evaluation,' at least not in my usage in an 'if' test scenario. Not a big deal to work around it, just a difference from C++ that you might mention in the existing documentation about the differences between FOAL and C++.