TransForm Programming Language Overview

Description

Introduction to the TransForm Programming Language, TPL, along with explantions of the data model, expressions, statements, etc.

 Overview

The TransForm Programming Language (TPL) is a means by which a form designer can specify the behavior of a TransForm form as the user fills it in.

TPL is a traditional programming language turned to the needs of TransForm forms. While not identical to any other programming language, its syntax and semantics should be familiar and easily learned by developers familiar with languages such as Basic, JavaScript, Java, and C.

TPL code is used in TransForm in two main ways: Standalone expressions to calculate values for display or condition testing and multi-line functions to respond to events such as field value changes or to be called in expressions or other functions.

This documentation gives a simple introduction to TPL, followed by more detailed explanations of various aspects of the language. The different sections are:

Samples of Use

Some simple examples of TPL programs.

Data Types

The types of data items and organization manipulated by TPL code.

Data Model

The particular data structures available to TPL code, including form data and local variables, and how it is accessed.

Expressions

The operators and operands that are used for computation.

Statements

The types of statements, including those for looping, testing conditionals, and flow-control.

Functions

Using built-in and user-defined functions in expressions.

Execution

The times when TPL code is executed and the restrictions sometimes imposed.

User-Defined Functions

Defining and using functions written by the form designer.

Built-in Functions

An overview of the functions built-in to TPL.

Why TPL?

Why an application-specific language was used instead of a popular, existing one.

 Samples of Use

Here are some...

 Data Types

There are a few different types of data that are handled by TPL code. These include individual values, such as strings of text, as well as groupings of data, such as records with fields and datagroups with one or more items each with their own group of fields and/or other datagroups. There is also the type of data returned from database queries and REST calls organized in JSON format. JSON format has values (strings, numbers, and true/false/null), objects, and arrays. Form data is stored as JSON, with datagroups being arrays of objects, and most field values stored as strings. TPL provides means for working with these different data types.

Most data used by TPL code is either text data, including text representations of numeric and date/time values, or JSON-style objects (including arrays). Operators and built-in functions are provided for working with this data. For example "+" is a binary operator that results in a text representation of the numeric sum of two values that are, or can be converted to, numbers, and "&" is a binary operator that results in a text value that is the concatenation of two values that are accessed as text strings. JSON data that has other types of values (such as numeric, boolean, and null) are interpreted in an appropriate way by operators.

In expressions and statements, numeric constants are represented as integers or decimal values. Text constants are enclosed in double-quote (") characters. Within the double-quotes, a double-quote may be represented by a backslash ("\") followed by a double-quote. Other escapes are "\n" for embedded newline, "\t" for a tab character, and "\\" for a backslash.

Date/time values in TransForm are usually represented by text in the form of "YYYY-MM-DD hh:mm:ss.s". This is a locale-independent "year/month/day hours:minutes:seconds" form that when sorted alphabetically also sorts by chronological time. The full-time, seconds, or fractional seconds values are optionally present. The time zone is assumed to be that where the device is running. Built-in functions are provided to facilitate conversion to and from other forms, such as "seconds since a particular date".

True/False values in TransForm calculations are usually represented by a blank value ("") for false, and "1" or any other non-blank value for true. The IF statement, and the "!", "||" and "&&" operators, all treat "" as false and all other values as true. The expression "4 < 5" results in "1" and "4 > 5" results in "". Note that the values of some form fields may need to be interpreted differently and they may require explicit text value testing, such as checking for "Yes" for true and "No" for false, or setting to those values as appropriate for the results of a calculation.

An example of some data is the following (expressed as JSON):

{
   "field1": "Value A",
   "field2": "Value B",
   "array1":
      [
         {
            "x": 10,
            "y": 20
         },
         {
            "x": 15,
            "y": 45
         }
      ],
   "obj1":
      {
         "a": "Some text"
      }
}

TPL code can access that data using the following syntaxes:

name

A name accesses that name/value pair at top level. For example, "field1" refers to the name value pair with value "Value A" and "array1" refers to the name/value pair with a value of the array with two elements. Names should generally start with an alphabetic character or "_".

objectValue.name

A "." between an object value (such as the name of an object) and a name accesses the name/value pair within the object. For example, "obj1.a" would have a value of "Some text".

The objectValue must evaluate to a reference to an object. If the objectValue refers to an element in an array that is not already defined within an existing array, then that element will be defined with an empty object as its value when used with the "." syntax. If the objectValue refers to an object and the name refers to a name that is not already defined within the object, then that name will act as if it is undefined when used. If the objectValue refers to an undefined name/value pair within a parent object, that name will be defined in the parent object with an empty object. This means that intermediate objects and array items are automatically defined when part of the path to a name/value pair. For example:

a = obj()  ' new, empty object
b = a.x    ' b gets "", a.x stays undefined
c = a.x.y  ' c get "", a.x is empty object
arrayValue[index]

An integer value enclosed by brackets after an array value accesses the element of the array specified by the index (0-origined). If it is an array of objects, the value is an object. If it is an array of scalar values (strings, numbers), the value is that scalar value. For example, "array1[1]" would access the element with and object with x=15 and y=45. If the element does not exist (index is greater than the length of the array minus 1) then the value is "".

If the index is negative, then it specifies an element of the array counting from the last element (-1 is the last element, -2 is the second from the last, etc.). If the index specifies an element before the first element of the array, then the value is the first element.

objectValue[nameAsString]

A text value enclosed by brackets after an object value accesses the name/value pair within the object. For example, "obj1["a"]" would have a value of "Some text".

The "nameAsString" may be either a string constant (characters surrounded by double-quotes) or a value that results in text.

stringValue[index]

An integer value enclosed by brackets after a string (text) value results in the single character at that position in the string (0-origined). For example, "field1[3]" would be the letter "u".

If the index is negative, then it specifies the character in a position counting from the end of the string, with -1 being the last character. For example, "field1[-3]" would be the letter "e".

Indexes specifying characters past either end of the string will return "".

Other examples are:

  •  Example 1

    Description of Example 1 goes here above the example itself.

    a = b
  •  Example 2

    a = b.c

 Data Model

There are several classes of data that a TPL program needs to access. These include the data fields of the form being edited, information about the form (such as its status), and temporary data to be used as part of computing various results. TPL provides means for explicitly referring to each of the different classes of data.

Each of the different classes of data are organized as JSON-style data. That is, they are an object, with name/value pairs. The values may be text, objects, or arrays of objects.

To better understand the different classes, it helps to look at the "lifecycle" of filling out or updating form data and the different times in that lifecycle when TPL code is executed. We'll call this the Form Editing Lifecycle.

The Form Editing Lifecycle starts when a form (also known as a "form instance") is chosen from the Existing Forms List in the Form Filler and opened for editing by either double-tapping or using the Edit command in the Main Control Bar above the list. This will bring up the Form Details Screen which shows the contents of that form instance. The Form Details Screen shows some or all of the form's fields and their current values, optional descriptive and help information, and buttons for displaying "pages" with additional fields and performing certain operations such as uploading and submitting the form to the server.

Before the form is opened for editing, optional "ON_LOAD" TPL code may be executed.

Each time the Form Details Screen is displayed, as part of assembling that display TPL code may be executed to fill in placeholders within various blocks of text. A placeholder consist of a standalone TPL expression enclosed within brace characters ("{" and "}") within the text. For example, the text of a heading may include the placeholder "{field1}" which would be replaced by "Value A", or it may include "{len(field1)}" which would be replaced by "7".

As part of assembling the Form Details Screen, there may be IF commands as part of the form type definition to conditionally show and hide parts of the form. (Note: These are form IF commands in the form type's Commands List, not TPL IF statements in TPL code.) Those IF commands include a standalone IF TEST TPL expression that is evaluated each time the screen is reassembled. If that expression is True (any value other than "") then the commands up until an ELSE or ENDIF are executed to display fields, etc. Otherwise, those intermediate commands are skipped.

In addition to IF TEST expressions, some other form commands allow the use of Form Display Expressions as part of rendering their display and operation. The type of value needed and the way in which that value is used depends upon the command. For example, a List field may optionally obtain the list of items to display from a calculated array of objects instead of from a fixed list entered at the time the command was created by the form designer, while another field may be displayed in a color that is determined by a simple text value.

When certain editors are opened by the user to edit a field value, such as by tapping on a List Field, or by tapping the "Next" or "Previous" button in another editor to change to the editor, optional "ON_EDITOR" TPL code may be executed.

When a field's value is changed by the user, such as by typing in a new value and tapping the Confirm, Next Field, or Previous Field button, optional "ON_CHANGED" TPL code may be executed.

When certain buttons that are part of the form are tapped, optional Button Execution TPL code associated with that button is executed.

Finally, when the form is finished being edited, and the display is being changed back to the Existing Forms List, optional "ON_FINISHED" TPL code may be executed. (Not implemented yet.)

During the Form Editing Lifecycle, there are certain classes of data that relate to the particular form instance being edited. These classes of data are:

Form Data

Form Data is the JSON object that contains the current values of all of the form's fields. Within this object may also be arrays of objects for data groups that are part of the form, such as information about multiple photos or line items.

Group Data

Group Data is a JSON object that contains the current values of just a single item within a Data Group. Since there can be more than one item in a Data Group, this provides a simple way to refer to related fields within that item. The particular Data Group is the one that contains the field being edited, the text placeholder associated with the heading being evaluated, any IF commands being evaluated, etc. Group Data includes any sub-data groups within the item. For TPL code running for ON_LOAD and ON_FINISHED and when there are no enclosing data groups associated with the TPL code, Group Data is the same as Form Data; that is, it refers to all of the form's fields and arrays of data.

Metadata

Metadata is made up of some of the metadata fields for the form instance plus some other values. This includes the current status value for the form, the date/time created, the user ID (an email address) of the person assigned this form, and more.

In addition to the form instance data, there are other classes of data:

Local Data

Local Data is an object that may be used to hold temporary values needed during execution of a single TPL function. For example, the name/value pair named "i" could be used to hold the counter in a FOR loop and one named "result" could be used to hold the result object from an Ajax call (with "result.error" indicating any errors and "result.responseText" containing the server response as text). A different instance of the Local Data object is created for each function invocation or standalone expression. So, for example, a recursive function would have a new instance for each call, with the previous instance restored on return.

Global Data

Global Data is an object that is persistent throughout Form Editing Lifecyle. A different instance is created each time the lifecycle starts. The object may be used to hold temporary values that need to be shared among different TPL code functions and standalone expressions.

System Data

System Data is an object that exposes various values representing the current state of the Form Filler. This includes the current user ID and display name, account ID and display name, the "path" of data groups and objects starting at top-level Form Data and ending in Group Data, and more.

The first characters of a name specify which of the different classes of data is being referred to in TPL code:

LocalData

References to plain names (starting with an alphabetic character or "_") are assumed to refer the current function's temporary Local Data. Local Data values may be scalar values, like text, or arrays or objects. Local Data is an empty object when a function starts executing. New name/value pairs may be created by assigning to the name. See the description of assignment statements in the section on Statements and the sample code examples.

^GlobalData

References to names preceded by a single "^" character are assumed to refer the current Form Editing Lifecycle's Global Data. Global Data values may be scalar values, like text, or arrays or objects. Global Data is an empty object at the start of editing a form instance. New name/value pairs may be created by assigning to the name. See the description of assignment statements in the section on Statements and the sample code examples.

#FormData

References to names preceded by a single "#" character (e.g., "#field1") are assumed to refer the current Form Data. The entire form data object is available, e.g., "#array[1].y" would refer to the value of "y" in the second item in data group "array1".

##GroupData

References to names preceded by two "##" characters (e.g., "##x") are assumed to refer the current Group Data.

$#Metadata

References to names preceded by the two characters "$#" (e.g., "$#status") are assumed to refer the current form Metadata. The metadata is presented as an object with name/value pairs representing copies of form instance data. (This means that assigning to them does not change the actual value in the form.) The metadata items are:

$#accountid

The account ID associated with the form instance.

$#created

The date/time the form instance was created.

$#comments

The representation of the JSON for the comments associated with the form instance.

$#completed

The date/time when the status was last changed.

$#formid

The Form ID of the form type being applied to the form data.

$#forminstanceid

The unique ID of the form instance.

$#formversion

The not currently used. If form IDs have versions, the version used for the data will be here to allow for modified versions to be handled.

$#haserrors

The flag that indicates whether or not an "error" has be set for the form instance. This is "Y" if true, "" if false.

$#missingrequired

The flag that indicates whether or not a "required" field has been detected that has no value. This is "Y" if true, "" if false.

$#nofiller

The value of the "no filler" setting for the form instance (set, for example, using the Management Console). The value is "Y" if true, and "N" or "" if false. This should usually be "N" or "" in the Form Filler.

$#person

The User ID of the person assigned this form instance, usually an email address and usually the logged in user. See the System Data $username and $userdisplayname values.

$#status

The current status of the form instance.

$#timestamp

The date/time of when this form instance was last inserted or updated in the server database. Blank if not yet saved.

$SystemData

References to names preceded a single "$" character (e.g., "$username") are assumed to refer the current form System Data. The data is presented as an object with name/value pairs representing copies of the various values. (This means that assigning to them does not change the original value in the system.) The System Data items are:

$accountdisplayname

The display name for the account the user is currently logged into.

$accountid

The account ID that the user is currently logged into.

$editfieldname

The name of the field variable when running as "ON_EDITOR" and "ON_CHANGED". Otherwise, "".

$formdata

The Form Data object. The same as what you get from "#" except that you can also use brackets after it to access top-level fields using a text value in a variable. The values within the object are read/write and changing them does change the form's data.

$formdisplayname

The display name for the current form's form type.

$formid

The form ID for the current form's form type.

$groupcount

The number of items in the current Group Data's parent array. See the related system data values $groupindex, $groupname, and $grouppath.

$groupdata

The Group Data object. The same as what you get from "##" except that you can also use brackets after it to access top-level fields within the data group using a text value in a variable. The values within the object are read/write and changing them does change the form's data.

$groupindex

The index (0-origined) of the current Group Data within its parent array. See the related system data values $groupcount, $groupname, and $grouppath.

$groupname

The Name command property of the data group containing the current Group Data in Form Display Expressions. The Array Name of the data group containing the current Group Data in most other cases.

$grouppath

A specification of the explicit "path" from Form Data to Group Data. This is an array of text and numeric values. The text values specify the names of the parent objects or arrays and the numeric values specify the element indexes (0-origined) within arrays. For example:

["array1",1]

would be the value of $grouppath if array1[1].x was being edited and array1[1] was the Group Data.

It is assumed that code using $grouppath is written knowing the general structure of the fields, objects, and arrays in the path for that form type. The $grouppath value is useful, for example, to access fields in a Group Data's parent or to make use of the index(es) of Group Data in its parent(s).

Related system data values are $groupcount, $groupindex, and $groupname.

$haserrors

The flag that indicates whether or not an "error" has be set for the form instance. This is "Y" if true, "" if false.

$language

The selection of which language to use when multi-langague support is available for an account's forms.

$languages

An object with information about the languages that may be supported by form types for this account, with name/value pairs specifying the short name and display name.

$lastdefsdownloadtime

The date/time when form type definitions were last downloaded from the server for this account. If none downloaded, this will be blank.

$lastrefresh

The date/time when all form instances were last downloaded (or refreshed) from the server for this account. If downloading was not done, this will be blank.

$lastsync

The date/time when any forms modified or created since last login were last uploaded to the server for this account. If no sync has occurred, this will be blank.

$missingrequired

The flag that indicates whether or not a "required" field has been detected that has no value. This is "Y" if true, "" if false.

$nowait

The flag that indicates whether execution show avoid situations that will require waiting for completion. This is "1" if true (do not wait), "" if false (waiting is permitted). See the description of "Execution" in this language documentation.

$userdisplayname

The display name for the currently logged in user.

$username

The user ID for the currently logged in user, usually an email address.

 Expressions

Expressions in TPL are similar to most computer languages. They are a combination of explicit constant values, references to variable values, operators, and functions. An expression is evaluated by applying the operators and functions to the values and any intermediate values, resulting in the value of the expression itself. The order of execution of the operators is determined by certain rules which include the use of parenthesis to override the defaults.

An expression may consist of a single value, such as just the number "5" or a reference to a field like "field1", and not involve any operators or functions.

The value of an expressions may be a simple, scalar value, such as text, or it may be a reference to an JSON-style object or array.

Expressions are used in TransForm for a variety of purposes. They are used by IF commands in form type definitions to control hiding and showing parts of the form. They are used in placeholders within various blocks of text to, for example, provide text that varies depending upon the value of a field. They are used in statements that make up programs executed to respond to events such as field editing.

The operators are (in decreasing order of precedence):

+ - !

Unary plus, minus, and "not". Plus and minus attempt to convert the value to a number, or else 0. Plus results in the number as text. Minus results is the negative of the number as text. (minus of 1 is -1, minus of -1 is 1). "Not" treats the value as text and returns the text value "1" (true) if the value is blank and "0" (false) otherwise.

* /

Multiply and divide. Both attempt to convert the value to their left and the value to their right to a number, or else 0. Multiply results in the product of the two. Divide results in the division of the left by the right. The results are text representations of the result values. For example:

2.00 * 3.00

will result in the text value "6". To control precision or formatting, use functions like ????.

+ - &

Add, subtract, and concatenate.

Add and subtract attempt to convert the value to their left and the value to their right to a number, or else 0. Add results in the sum of the two. Subtract results in subtracting the right from the left. The results are text representations of the result values. For example:

1.00 + 2.00

will result in the text value "3". To control precision or formatting, use functions like ????.

Concatenate converts the value to its left and right to text. The result is the left value followed by the right. For example:

"abc" & "def"

will result in the text value "abcdef".

< <= >= >

Less than, less than or equal to, greater than or equal to, and greater than. These comparison operators attempt to convert the value to their left and the value to their right to a number ("" is treated as zero), or else they are left as text. If both values are numeric then the test is a numeric comparison. If either value is not convertible to a number then the test will be the text comparison of the two values converted to lower-case. The result is the text value "1" for true and "" for false. For example:

15 > 7

will result in the text value "1" (true), and:

"abc" > "ABC"

will result in the text value "0" (false) because they are equal when converted to lowercase.

== != === !==

Equal, not equal, text equal, and text not equal.

The equal and not equal comparison operators attempt to convert the value to their left and the value to their right to a number ("" is treated as zero), or else they are left as text. If both values are numeric then the test is a numeric comparison. If either value is not convertible to a number then the test will be the text comparison of the two values converted to lower-case. The result is the text value "1" for true and "" for false. For example:

15 == "15.00"

will result in the text value "1" (true), and:

"abc" != "ABC"

will result in the text value "0" (false) because they are equal when converted to lowercase.

Text equal and text not equal comparison operators treat both values as text and are case-sensitive. The result is the text value "1" for true and "" for false. For example:

15 === "15.00"

will result in the text value "0" (false), and:

"abc" !== "ABC"

will result in the text value "1" (true).

&& ||

"And", and "or".

"And" and "or" attempt treat the values to their left and right as false ("") or false (non-blank). The results are either "1" (true) or "" (false). "And" is true if both values are true. "Or" is true if either value is true. For example:

2 < 3 && 4 < 3

will result in "0" (false), while:

2 < 3 || 4 < 3

will result "1" (true).

 Statements

A function written in TPL is made up of one or more statements. Each statement goes on its own line.

For example, this is an assignment statement followed by an IF statement, another assignment statement, and an ENDIF statement:

a = len(b)
IF a > 10
   c = "long"
ENDIF

To aid readability, statements may be extended to more than one line by ending a line with a "\" character. The newline character after the "\" (and the "\") will be ignored. Additionally, to provide for comments, any characters after an apostrophe (') up until the end of the line will be ignored.

This example uses both a continued line and a comment:

IF a >= 10 && \
   a < 20 ' See if a is within range

There are three types of statements: Expression Statements, Assignment Statements, and Keyword Statements.

An Expression Statement is just a plain expression on a line by itself. Normally, when used as part of multi-line code, this type of statement would be a function call for the purpose of the side-effects of the call. (Since normal operators like "+" and "-" have no side-effects, and the value of expression statements are not used once executed, statements like "2 + 2" are relatively meaningless.)

An example of an expression statement, which uses the built-in function "showWarning", is:

showWarning("Value \"" & #field1 & "\" is out of range. Please try again.")

An Assignment Statement is similar to assignment statements in many other computer languages. It consists of a left-side reference which will receive a value, as assignment operator, and a right-side expression. The right-side expression is evaluated and then the left-side reference is used by the assignment operator.

Some examples of assignment statements are:

' Set value of local name v1 to "4":
v1 = 2 + 2

' Set field z of the 1st item in data group array1 to "red":
#array1[0].z = "red"

The left-side must evaluate to a reference to a name in a name/value pair of an object or an item in an array. It may not be a plain value, such as a text or numeric constant, or an expression with operators. If it refers to a name that is not already defined within an object, then that name will be defined. If it refers to an element in an array that is not already defined within an existing array, that element will be defined.

There are four different assignment operators:

a = b

Assigns the value of the expression on the right to the reference on the left. Note that the expression on the right may be a scalar value or a reference to an object or array. If it's a reference, the reference to the object is used.

For example:

a = 2 + 2      ' Sets local name a to "4"
b = array1[0]  ' Sets b to an object with x=10 and y=20
b.x = 5        ' array1[0].x is now "5"
c = array1     ' Sets c to an array
c[0].y = 6     ' array1[0].y is now "6"
a += b

Adds the value of the expression on the right to the current value referenced on the left and then assigns that sum to the reference on the left.

For example:

a = 1   ' Sets local name a to "1"
a += 3  ' Sets a to "4"
a -= b

Subtracts the value of the expression on the right from the current value referenced on the left and then assigns that difference to the reference on the left.

For example:

a = 10  ' Sets local name a to "10"
a -= 3  ' Sets a to "7"
a &= b

Concatenates the current text value referenced on the left with the text value of the expression on the right and then assigns that combination to the reference on the left.

For example:

a = "test"  ' Sets local name a to "test"
a &= "ing"  ' Sets a to "testing"

A Keyword Statement is a mixture of one or more keywords and zero or more expressions. The keywords are case-insensitive text which are distinguished from the same letters used as field, variable, and function names by the syntax of the statement. Some keyword statements consist of a single, standalone keyword, such as "ENDIF" while others intersperse keywords and expressions, such as "RETURN 15". The style of this documentation is to show the keywords in all caps, but lower-case or mixed-case may be used if desired.

The statements for use within multi-line user code are:

IF condition ELSE ELSEIF condition ENDIF

An IF statement is used to execute or skip other statements depending upon the results of a conditional expression. It works in conjuction with an ENDIF statement and optional ELSE and ELSEIF statements which follow it.

The initial IF keyword is followed on the line by an expression. If the expression is true (not "") then the statements immediately following the IF statement are executed up until an ELSEIF or ELSE statement is encountered. Then all statements are skipped until an ENDIF is encountered.

If the IF condition expression is false ("") then the statements immediately following the IF statement are skipped until either an ELSEIF or ELSE statement is encountered. If an ELSEIF is encountered, then the conditional expression following that keyword on the line is executed and the process starts again. If an ELSE is encountered, then all statements following it are executed. If an ENDIF is encountered execution resumes.

An IF statement may be followed by any number of ELSEIF statements, but only one ELSE statement. All ELSEIF statements must precede the ELSE statement, if present. There must be a matching ENDIF statement at the end of the statements whose execution is controlled by the IF, ELSEIF, and ELSE statements.

IF statements may be nested. That is, the statements between an IF statement and the ENDIF may include additional IF/ENDIF sequences, with each ENDIF corresponding to the closest open IF.

For example:

msg = "There "
IF count==0
 msg &= "are no items"
ELSEIF count==1
 msg &= "is one item"
ELSE
 msg &= "are " & count & " items"
ENDIF
FOR v TO e FOR v TO e STEP s CONTINUE EXITFOR ENDFOR

The FOR and ENDFOR statements bracket a series of statements and control the repeated execution of those statements. (This process is commonly known as "looping" through those statements.) In addition, FOR and ENDFOR control the repeated incrementing of the value of a Looping Variable that may be used to differentiate each pass through those statements and determine when to stop executing the loop.

The FOR statement includes the keyword FOR, Looping Value Expression "v", the keyword TO, Ending Value Expression "e", and the optional combination of keyword STEP with Step Value Expression "s".

The Looping Value Expression is executed before the first time through the loop. A Looping Value Expression is either an assignment statement whose left side will then be used as the Looping Variable, or an expression that results in something that could be on the left side of an assignment statement which will be used as the Looping Variable and already has a value.

The Ending Value Expression is executed before each loop execution. It is compared numerically to the Looping Variable's value. If the Looping Variable's value is less than or equal to the Ending Value Expression the statements between the FOR and ENDFOR statements are executed. If the Looping Variable's value is greater than the Ending Value Expression the statements up to and including the matching ENDFOR statement are skipped and execution will continue with the statement following the ENDFOR statement.

When the ENDFOR statement is encountered in execution, the Step Value Expression is evaluated. That value, the Step Value, is used to increment the Looping Variable's value. (If there is no STEP keyword, or if the value is zero or non-numeric, the Step Value is assumed to be 1.) The Looping Value test is then performed to determine whether execution of the loop is to continue or be ended. If execution is to continue, it continues with the first statement after the FOR statement. If execution of the loop is to be ended, execution continues with the first statement after the ENDFOR statement.

Note that if the Step Value is negative, the Looping Value is appropriately decremented and the ending test will be inverted so that Looping Values less than the Ending Value will end looping and Looping Values greater than or equal to the Ending Value will continuing looping.

Executing a CONTINUE statement in a FOR loop will end that trip through the loop and act as if an ENDFOR statement was encountered, incrementing and testing the Looping Value.

Executing an EXITFOR statement in a FOR loop will end that execution of the loop and skip to the statement following the FOR loop's ENDFOR statement.

For example:

' Calculate the sum of array1's x values
sum = 0
FOR i=1 TO len(array1)
 sum += array1[i-1].x
ENDFOR

' Reverse characters in a text value
txt = "Testing"
reverse = ""
FOR i=len(txt)-1 TO 0 STEP -1
 reverse &= txt[i]
ENDFOR
' Result is reverse="gnitseT"

 Functions

Like most computer languages, TPL expressions may make use of functions. Functions are included within an expression in most places where a value may appear. They consist of a name followed by zero or more expressions (called "arguments") within parenthesis, separated from each other by commas. The function returns a result that may be further used in an expression.

For example, here is an example using the math function "power":

a = 3
b = power(2,1+a)+1 ' 2*2*2*2+1 = 17

The returned value may be an object or array. That value may be saved and the name/value pairs or elements may be used.

For example:

' Assume this User-Defined Function:
FUNCTION @example1
  x = obj()     ' Set x to an empty object
  x.a = 1       ' Set name a in x to 1 
  x.b = 2       ' Set name b in x to 2
  return x
ENDFUNCTION

a = @example1() ' a gets an object
c = a.a + a.b   ' c gets "3"

There are two main classes of functions: Built-in Functions and User-Defined Functions. Built-in Functions are part of the TPL implementation. Their names always start with an alphabetic character. For example, "len" is the name of a function that returns the number of elements in an array value or characters in a text value. User-defined functions are written by the user of TPL (the person who defines form types and uses the TransForm Designer in TransForm Central) as a series of TPL statements bracketed by FUNCTION and ENDFUNCTION statements. The names of user-defined functions always start with the "@" character. This way, there is no chance that a person will create a user-defined function whose name will conflict with that of a built-in function that they do not know about or that is added to the system at a later point.

Some functions have side-effects that are specific to the TransForm system. For example, a function may set a value in system data, access a server on the Internet, or display a message on the screen. Sometimes functions like these may appear by themselves as an Expression Statement and only return a meaningless value, such as "". Other times they may return an object with information (including any error information) which would then need to be assigned to a variable for further processing.

The value of arguments may be text or other simple values, or they may be JSON-style objects including arrays. The invoked function may read the value of all arguments. The invoked function may also reference and set name/value pairs within object arguments and elements within array arguments.

For example:

' Assume this User-Defined Function:
FUNCTION @example2
  x = args(1) ' First argument
  y = args(2) ' Second argument (an object)
  return x + y.x
ENDFUNCTION

a = @example2(array1[0].x, array1[1]) ' a gets "25"

References that may appear on the left side of an assignment statement, sometimes called "L-Values" in Computer Science (with the "L" standing for "Left"), may be used as arguments. Some functions may set new values for those passed as L-Values as needed.

For example:

' Assume this User-Defined Function:
FUNCTION @example3
  a1 = args(1)          ' Copy original value
  args(1) = "New value" ' Assign new value
  return a1             ' Return original
ENDFUNCTION

a = "Old value"
b = @example3(a) ' b gets "Old value", a is "New value"

 Execution

TransForm executes various TPL expressions and sequences of statements throughout the Form Editing Lifecycle. TPL code is executed synchronously. That is, statements and expressions are executed one at a time, one after another. A function executes from start to finish, relinquishing execution to other code only when it calls that code explicitly as a function. During the time that the code is executing the user interface does not respond to any user input. There is no response to button taps until execution completes.

TPL code execution is usually quite quick so the delay in response is not perceptible to the user. However, there are two cases where the user may notice a delay.

The first case is when the code executes a large number of statements. While normal execution on common devices can run at hundreds of thousands of statements per second, with typical placeholder, IF TEST, ON_CHANGED, and other code finishing in milliseconds or less, the form designer may specify code that takes a longer time (perhaps inadvertently). Care should be taken to avoid such situations if possible, such as by performing long calculations in advance at a time when a delay is more acceptable. (See the use of the "wait" function.)

The other case where the user may notice a delay is when a built-in function accesses services that have an inherent delay. For example, programmatic interactions over the Internet have communications and server delays due to latency, bandwidth, and load. On-device databases may have delays because of complex queries on large datasets.

In this second case, TransForm makes the delay visible to the user through a simple "Please wait..." display, such as covering the display with a transparent overlay and showing an animation.

In many situations, having a potential long inherent delay is not acceptable. Execution at these times is considered "No Wait Execution" and there is a read-only System Data value ("$nowait") that is true at those times. Built-in functions that may have such delays check before execution to see if delays are allowed. If delays are not allowed, the functions return immediately with an appropriate value reflecting that situation, such as an empty value or a specific error indication. User-defined functions can check the $nowait value and behave differently, such as using a default value instead of checking a database.

During the Form Editing Lifecycle, No Wait Execution is required when evaluating placeholders, IF TESTs, and Form Display Expressions. Waits are allowed when executing ON_LOAD, ON_EDITOR, ON_CHANGED, Button Execution, and ON_FINISHED code.

 User-Defined Functions

The form designer...

 Built-in Functions

Here is an interim list of the built-in functions:

ajaxGET(url, timeout)

Does an Ajax request to "url". Optional timeout of "timeout" milliseconds. Returns an object with attribute "error" set to text if an error or "" otherwise. If no error, then also includes XMLHttpRequest attributes: status, statusText, timeout, responseType, and responseText.

args(argnum)

Returns argument number "argnum" to the current function.

array()

Returns a new empty array object.

dblookup(t, f, c, v)

This temporary demonstration function looks within a built-in database for table "t" and returns an array objects, one per row where field "f" compared with comparison "c" to value "v" is true. The only table is "products" and the fields are "id", "name", "author", and "price". The comparisons are "==", "!=", "<", "<=", ">=", ">", "starts", "ends", "!starts", "!ends", "contains", and "!contains".

indexOf(text1, text2)

Searches for the first occurence of text2 within text1. Returns the position within text1 where text2 starts (0-origined) or "-1" if there is no occurence. If text2 is "", returns "0".

JSONparse(text)

Returns parsed JSON as an object or array. Errors return "".

len(array) len(text)

Returns the number of the highest numbered element in an array or the number of characters in a text value.

msgHideProgress()

Removes modal message box shown by msgShowProgress(), if any. Execution continues immediately. Returns "1".

msgShowBasic(title, body, confirm)

Displays a modal message box with a title and a body. The button to dismiss will be labeled "OK" if the optional confirm is missing. Execution pauses until the user responds. Returns "1".

msgShowConfirm(title, body, confirm, cancel)

Displays a modal message box with a title and a body. The button to proceed will be labeled "OK" if the optional confirm is missing and the button to cancel will be labeled "Cancel" if the optional cancel is missing. Execution pauses until the user responds. Returns "1" for confirm and "" for cancel.

msgShowConfirmWarning(title, body, confirm, cancel)

Displays a modal message box with a red title and a body. The button to proceed will be labeled "OK" if the optional confirm is missing and the button to cancel will be labeled "Cancel" if the optional cancel is missing. Execution pauses until the user responds. Returns "1" for confirm and "" for cancel.

msgShowProgress(title, body)

Displays a modal message box with a title, a body, and a spinning icon. There is no button to dismiss. Execution continues immediately. Returns "1". Use msgHideProgress() to remove the modal message box.

msgShowThreeButton(title, body, one, two, three)

Displays a modal message box with a title and a body. The first button to dismiss will be labeled "1" if the optional one is missing. The second button to dismiss will be labeled "2" if the optional two is missing. The third button to dismiss will be labeled "3" if the optional three is missing. Execution pauses until the user responds. Returns the label of the chosen button.

msgShowWarning(title, body, confirm)

Displays a modal message box with a red title and a body. The button to dismiss will be labeled "OK" if the optional confirm is missing. Execution pauses until the user responds. Returns "1".

nargs()

Returns the number of arguments to the current function.

now()

Returns the current date/time as yyyy-mm-dd hh:mm:ss.

obj()

Returns a new empty object.

random()

Returns a random number great than or equal to 0 and less than 1 as text.

split(text, separator)

Returns an array of "text" broken on character(s) "separator".

substr(text, start, length)

Returns the length number of characters in text starting at character start (0-origin). If length is missing, then all of the rest of the characters in text will be returned. If start is negative, then the start is calculated from the end of text, with "-1" being the last character, "-2" the next to last, etc.

substring(text, start, end)

Returns the characters in text starting at character start (0-origin) up to but not including character end (0-origin). If end is missing, then all of the rest of the characters in text will be returned. If start or end is negative, then the respective values are calculated from the end of text, with "-1" being the last character, "-2" the next to last, etc.

updateCSS(additionalRules)

Ensures that there is an "additionalCSS" style sheet and then replaces the items there with those in additionalRules. Each rule is a separate line in additionalRules including the selector(s), braces, and the rest of the style declaration.

For example:

.formitemtitle {font-size:120%;color:red;}
wait(duration)

Pauses execution for duration seconds. The value of duration should be a number between 1 and 60. It will be adjusted to conform to that range. Returns the adjusted value of duration.

 Why TPL?

A variety of factors went into the decision to create and use an application-specific language system for use in TransForm. This is both the language itself and the implementation of the language system.

Choosing an existing language opens the possibility of making use of an existing implementation, such as using the JavaScript engine built into mobile OS environments. A problem with an existing implementation is that most are not available in a language supported by the runtime environment of TransForm. If an existing implementation is not used, then there is the difficult challenge of creating and maintaining a compatible implementation that provides all of the features and behaviors (documented or not, needed for use working with forms or not) of other implementations. Without compatibility and full implementation, much of the advantages of an existing language are lost and the promise of "being standard" is not fulfilled. Hope that learning the language for use in TransForm will provide a strong basis for claiming skill for direct use elsewhere would then be suspect.

Another important factor was the need to have complete control of the implementation in order to help enforce security. The writers of the programs need to be able to be normal, line-of-business people, unvetted and perhaps unsupervised by an IT department. The language system needed to be able to be limited in functionality that could be abused, such as to leak data or operate maliciously while using the credentials of another user logged in with different roles.

Most common languages, including using JavaScript already part of the TransForm system, are designed to provide as much functionality as possible and would need to be cut back from their normal state. At that point, they would no longer be the same and would require special training to learn which features were changed or missing. Documentation elsewhere would be misleading. Code developed elsewhere could have anomolies when run in the restricted implementation. The promise of "write once, run elsewhere" would not apply without careful, knowledgeable checking in each case.

Another issue was the need to integrate the program execution with the form editing of TransForm. The normal execution engines of a language implementation would to need to be modified to meet those needs. The semantics of certain operations, and the definition of popular functions, would need to be changed.

An advantage of using a language system designed specifically for TransForm is that it can be tuned to the common operations performed when filling out forms. The library of functions can also be tuned to the data types being operated on. Features not required to meet the needs of TransForm users need not be implemented.

A desirable trait of a language used for TransForm is that it be easy for experienced programmers, familiar with other computer languages, to read and learn to use. That drove the use of explicit, word-based constructs such as "IF/ENDIF" and "FOR-TO-STEP/ENDFOR", and common expression and function-calling syntax.

Many of the form designers will not be directly using the language in any case. They will be using dialog-driven user interfaces to specify the operations to be performed, such as the construction of a conditional expression used to show or hide items on a form. For these users, the choice of language is not relevant at all, and hidden. However, if they do want to switch to using the language directly in a particular instance, the auto-generated code can be examined to provide a working baseline from which to start.

For all these reasons, and more, the decision to use TPL was made.