User-Defined Formula Language
MCS Software provides a formula language in used by various imports and exports throughout our suite of applications. This formula language is intended to produce a single output value from available input values. The language is not white-space, line-break, or case sensitive. Using an informal BNF, the language can be described in the following way: <formula> :: = <statement> [ <statement> [ <statement> ... ] ] <statement> ::= RETURN <expression> | IF <expression> THEN <statement> [ ELSE <statement> ] | BEGIN <statement> [ <statement> [ <statement> ... ] ] END | WHILE <expression> <statement> | VAR <variablename> [ = <expression> ] | SET <variablename> = <expression> | TRY <statement> CATCH <statement> | THROW <expression> | FOREACH <variablename> IN <expression> <statement> | BREAK | TRACE <expression> | GLOBAL <variablename> [ = <expression> ] <expression> :== <literal> | <constant> | <variablename> | <fieldname> | <functionname> ( <expression> [ , <expression> [ , <expresion> ... ] ] <expression> <binary_operator> <expression> | <unary_operator> <expression> | ( <expression> ) <literal> :== [ - ] [ 0-9*] [ . 0-9* ] "string literal" | true | false | null <constant> := @@identifierName <variablename> :== @identifierName <fieldname> :== [ fieldName ] <functionname> :== one of the following functions <binary_operator> :== one of the following operators <unary_operator> :== one of the following operators Comments Comments may be added anywhere in within a formula body using C/C++ style comment notations Execution Each statement within a formula is executed until a RETURN statement is encountered. The expression supplied to the first executed RETURN statement is the result of the formula. If no RETURN statement is executed, the result of the formula is NULL. Each statement type is described below: Stops execution of the formula and returns <expression> as the result For example: if <expression> is true then execute statement 1. If the optional ELSE portion is present, then statement2 is executed if <expression> is not true For example: Combines multiple statements into a single statement. This is useful if you need a conditional execution of more than one statement. For example: Executes a statement in a pre-test loop. If <expression> is true, then statement is executed and the test repeated until <expression> returns false For example: Declares a variable and optionally initializes it to a value For example: Sets a previously declared variable to a new value For example: Execute statement1. If an error occurs while executing statement1, statement1 is immediately aborted and statement2 is executed For example: Raises an error / exception For example: Iterates through an array and executes a statement for each item in the array For example: Breaks out of the current innermost WHILE or FOREACH loop. For example: Writes the value of <expression> to the trace log. This is useful for debugging a formula. When using the 'Test' button to test a formula, the trace log is displayed along with the results. Trace events are logged to log4net as DEBUG events for all formula evaluations, whether test or production. For example: Declares a global variable and optionally initializes it to a value. Global variable persist their values within the same formula body over multiple records. They are not shared between different formulas. The initial value is only used to initialize the global variable. It does not reset the value every time the statement executes. Use SET to change or reset the value of a global variable. For example (running summary operations): Or (accessing previous records):function name meaning parameter1 parameter2 parameter3 parameter4 IIF Conditional If, if parameter1 is true then returns parameter2 otherwise returns parameter3 condition expression to return if condition is true expression to return if condition is not true Coalesce returns the first non-null parameter first expression to check if not-null second expression to check if not-null (optional) 3rd expression to check (optional) 4th expression to check (you can have an unlimited number of parameters) NullIf returns NULL if parameter1 equals parameter 2, otherwise it returns parameter1 value to return if not equal to parameter2 value to test parameter1 against NullIfError returns NULL if the evaluation of parameter1 throws an exception, otherwise it returns parameter 1 expression to evaluate Date returns a new date value from the supplied Gregorian calendar components year month day Now returns the date and time right at the time the formula evaluation began (this value does not change when called multiple times with a given formula evaluation context, such that if used on an export all rows would have the same value) AddDays returns the result of adding a number of days to a date starting date number of days to add AddMonths returns the result of adding a number of months to a date starting date number of months to add AddYears returns the result of adding a number of years to a date starting date number of years to add Day returns the day of the month of a given date date Month returns the calendar month of a given date date Year returns the calendar year of a given date date DayOfWeek returns the day of the week (sunday = 0, monday = 1, ... saturday = 6 ) of a given date date ParseDate converts a string to a date string value to parse (optional) the expected format of the string. For example MM/dd/yyyy Len returns the length of a string; or, the number of elements in a list/array string value or array value Left returns a portion of a string starting from the left string value maximum number of characters to return Right returns a portion of a string starting from the right string value maximum number of characters to return Substring returns a portion of a string starting at a given position (this function will error if you specify a start and/or length beyond the range of the string) string value the starting index (beginning with 0) (optional) the number of characters to return Replace returns a the result of a string search-and-replace operation (case-sensitive) string value to search substring to search for within parameter1 string value with which to replace any occurrences of parameter2 IndexOf returns the first occurrence of a search within a string (-1 means not found, >= 0 means the index within the string that the match was found) string value to search substring to search for (optional) the starting position (beginning with 0) to begin searching. (optional)true for case-sensitive, false for case-insensitive StartsWith returns true if a string starts with a given substring, false otherwise string value to check substring to search for (optional) true for case-sensitive, false for case-insensitive EndsWith returns true if a string ends with a given substring, false otherwise string value to check substring to search for (optional) true for case-sensitive, false for case-insensitive StringCompare returns -1 if the parameter1 is alphabetically less than parameter2, 0 if they are equal, and 1 if parameter2 is greater than parameter1 first string to compare second string to compare (optional) true for case-sensitive, false for case-insensitive ToUpper returns the result of converting any lower case letters in parameter1 to upper-case letters string value ToLower returns the result of converting any upper case letters in parameter1 to lower-case letters string value Trim returns the result of removing any leading or trailing white-space characters from parameter1 string value PadLeft returns the specified string padded to the left to the specified length if the string is less than that length string value minimum length to pad to (optional)character to use for padding PadRight returns the specified string padded to the right to the specified length if the string is less than that length string value minimum length to pad to (optional)character to use for padding MatchRegex returns whether or not the specified string matches the supplied regular expression string test value regular expression (ECMA script) (optional) true for case-insensitive, false for case-sensitive ReplaceRegex returns the result of a regular expression replacement string value regular expression match string (ECMA script) regular expression replacement string (optional) true for case-insensitive, false for case-sensitive ParseFirstName returns the first name component of the supplied full name full name to parse ParseLastName returns the last name component of the supplied full name full name to parse ParseMiddleName returns the middle name component of the supplied full name full name to parse ParseSuffix returns the suffix component of the supplied full name full name to parse Format returns the result of formatting zero or more supplied values into a string using a standard .net formatting string. Parameter1 is returns with { n } replaced by value-n, and { n : format } replaced by value-n formatted by the specified format. For example Format("{0} + {1:0.00} = {2:0.0}", 1, 2, 3) would return "1 + 2.00 = 3.0" .net style format string (optional) value 0 to apply to the format string (optional) value 1 to apply to the format string (optional) value 2 to apply to the format string. You can supply an unlimited number of value Round returns a numeric value rounded to a given number of decimal places. Midpoints are rounded away from 0 (e.g. 2.5 rounds to 3) numeric value to round (optional) number of decimal places to round to (0 is the default) Truncate returns the integer portion of a number, discarding any fractional amounts (can be thought of as rounding towards 0) numeric value Ceiling returns a numeric value rounded up to the first integer greater than or equal to it (can be thought of as rounding towards positive infinite) numeric value Floor returns a numeric value rounded down to the first integer less than or equal to it (can be thought of as rounding towards negative infinite) numeric value Frac returns the fractional portion of a number numeric value Abs returns the absolute value of a number numeric value GetProperty gets the specified property of an object object value string property name GetPropertyNames gets an array of all of the properties in an object object value HasProperty gets whether or not the specified property exists in an object object value string property name GetListItem gets the specified array/list item array value CurrentRecord returns the current field values wrapped up as an object operator meaning + if the left-hand operand is a string, then string concatenation, otherwise numeric addition - subtraction * multiplication / division \ integer division % modulus division ( the remainder after integer division) | bit-wise OR & bit-wise AND ^ bit-wise XOR OR logical OR AND logical AND XOR logical XOR = equals <> not equal > greater-than >= greater-than or equal < less-than <= less-than or equal operator meaning - negative Not logical NOT ~ bit-wise NOT VAR @A = 3 //Everything after the two slashes on this line is considered a comment and is not executed
RETURN /*everything between the slash-stars is a comment and not executed*/ "Hello World"
RETURN <expression>
RETURN "hello world"
IF <expression> THEN <statement1> [ ELSE <statement2> ]
IF @A > 4 THEN
RETURN true;
ELSE
RETURN false;
BEGIN <statement> [ <statement> [ <statement> ... ] ] END
IF @A > 4 THEN
BEGIN
IF @B < 3 THEN
RETURN "hello"
IF @B < 5 THEN
RETURN "world"
END
ELSE
RETURN "hi"
WHILE <expression> <statement>
WHILE @A > 10
SET @A = @A - 3
VAR <variablename> [ = <expression> ]
VAR @A = 3
SET <variablename> = <expression>
SET @A = @A + 4
TRY <statement1> CATCH <statement2>
VAR @A = 0
TRY
RETURN 4 / @A
CATCH
RETURN NULL
THROW <expression>
IF @A = 0 THEN
THROW "Unexpected value"
ELSE
RETURN @A
FOREACH <variable> IN <expression> <statement>
VAR @CountNoIncome = 0
FOREACH @Student in [Students] BEGIN
IF GetProperty(@Student, 'NoIncome') = True
SET @CountNoIncome = @CountNoIncome + 1
END
RETURN @CountNoIncome
BREAK
VAR @NoIncomeExists = FALSE
FOREACH @Student in [Students] BEGIN
IF GetProperty(@Student, "NoIncome") = True BEGIN
SET @NoIncomeExists = TRUE
BREAK //break out of FOREACH and stop looking at the rest of the students
END
END
IF @NoIncomeExists BEGIN
END
TRACE <expression>
VAR @NoIncomeExists = FALSE
FOREACH @Student in [Students] BEGIN
TRACE Format("Checking student {0} for NoIncome flag", GetProperty(@Student, "Fullname"))
IF GetProperty(@Student, "NoIncome") = True
SET @NoIncomeExists = TRUE
END
RETURN @NoIncomeExists
GLOBAL <variablename> [ = <expression> ]
GLOBAL @RowCount = 0 //This does not get reset to 0 every time the formula executes, it only initializes the value to 0 the first time
SET @RowCount = @RowCount + 1
RETURN @RowCount //returns the row number of the record within an export
GLOBAL @PreviousStudent = NULL
VAR @Result
IF @PreviousStudent = NULL
SET @Result = NULL
ELSE
SET @Result = GetProperty(@PreviousStudent, "Fullname")
SET @PreviousStudent = CurrentRecord() //CurrentRecord() takes the current field values and places them in a object-value result with properties matching the field names
RETURN @Result //returns the Fullname field value of the previous student record