ArtFormula package contains two nonvisual Delphi component for symbolic expression parsing and evaluation. Provides runtime scripting engine for automating your programs. Also ArtFormula can symbolically evaluate derivatives of a function with basic simplification of a result.
Properties:
property Error : TFormulaError; - returns current error code.
TFormulaError = (ER_Ok, ER_ILLEGAL, ER_UNKNOWN, ER_RIGHT, ER_LEFT, ER_SYNTAX, ER_VARS, ER_NOTENOUGH);
property ErrPos : integer; - returns position of error.
property ErrLine : integer; - returns the line with error.
property ErrStrPos : integer; - returns the position of error inside source string.
property Stop : boolean; - set Stop to true during computation to stop.
property Compiled : string; - bytecode of compiled source string.
property LocalStrings : TStrings; - if TArtFormula compiled with multilang support (see below), this property contains list of pairs "StringName=LocalValue". You can use Names and Values properties of LocalStings to obtain string names and local values.
property ModuleName : string; - contains name of last called user module.
property FunctionName : string; - contains name of last called function. Useful for user defined modules and functions. By testing FunctionName value user function can recognize its name (along with module name by testing ModuleName property).
property ParamCount : integer; - contains parameters count of last called function (-1 for variable argument list function).
property SubFormula : TArtSFArray; - Set of compiled subroutines podused by CompileProgram procedure.
property Global : TArtSubFormula; - Global scope of ArtFormula program (for debugging issues mostly).
property SubRoutine : PArtSubFormula; - Pointer to current subroutine of ArtFormula program (for debugging issues mostly).
Published properties:
property AutoCreateVars : boolean; - if true, ArtFormula will create new variable with the first assignment statement. But if unknown variable is in the expression, ArtFormula will rise an expression.
property UnQuotedString : boolean; - if true, ArtFormula will consider unknown identifiers as string literals.
property TestUsedVars : boolean; - if true, ArtFormula will test whether all external variables, passed as vars parameter of Compile or Test procedure, are used in source expression.
property CaseSensitive : boolean; - if true, ArtFormula will distinguish case of characters in variables name. Note: functions names are always case insensitive.
property CommaDS : boolean; - if true, ArtFormula use comma as decimal separator instead of point. Note: if CommaDS set, use semicolons to separate function parameters.
property Step : boolean; - if true, you can stop computation, by setting Stop property to true. If false you can't stop computation until it ends.
property StoreLines : boolean; - if true, ArtFormula will store line number in compiled bytecode, so ErrLine will contain runtime error line number.
Note: Step, StoreLines and OnStep properties usefull for debugging issues (see demo).
property CaseSensitiveString : boolean; - if true, ArtFormula will distinguish case of string characters in comparison operations and in pos function.
property ExternGetVar : boolean; - if set, ArtFormula will use GetVarValue and GetVarsCount events to evaluate unspecified external variables.
property VarNameLiterals : string; - defines set of literals, allowed for variable names.
property NoLeadingZero : boolean; - if true, you can omit leading zero before point in floating point numbers (both .5 and 0.5 correct), if false - only notation with leading zero accepted (only 0.5, .5 - causes error).
property ZeroEmptyString : boolean; - if true, ArtFormula will treat empty string as 0 in arithmetic operations.
Methods:
procedure SetVar(name : string; value : PCalcItem); - sets value of variable, given by name parameter.
procedure SpecSetVar(name : string; value : PCalcItem; op : TAFsetOperations); - compound assigment procedure, combines value of variable with given value using the specified operation.
TAFsetOperations = (soPlus, soMinus, soMul, soDivision, soDiv, soMod, soConcat); - one of the operaions: +, -, *, /, DIV, MOD, concatenation.
function GetVar(name : string) : PCalcItem; - returns value of variable.
function IncVar(name : string) : PCalcItem; - increments value of a variable.
function DecVar(name : string) : PCalcItem; - decrements value of a variable.
procedure AddVar(name : string; value : PCalcItem); - adds internal variable.
procedure AddUserConstant(name, value : string); - adds new constant.
function AddUserFunction(name : string; paramcount:integer; fun : pformulafunction; defval : PDefArray = nil; ismodule:boolean=false;replace:boolean=false):PTableItem; - adds user function (see Section creating user functions and modules).
function AddModuleFunction(module: PTableItem; name : string; paramcount:integer; fun : pformulafunction; ismodule:boolean=false;replace:boolean=false;deffunc: string = ''):PTableItem; - adds module function (see Section creating user functions and modules).
function Test(instr : string; num : integer = 0; vars : PStringArray = nil) : boolean; - tests syntax of source code represented by instr. External variable names are passed as vars parameter, num specifies number of external variables. Returns true if there are no errors.
function Compile(instr : string; num : integer = 0; vars : PStringArray = nil) : string; - compiles source string and returns compiled bytecode. On error raises exception.
function ComputeN(num : integer = 0; vals : PCalcArray = nil) : double; - compute previously compiled program and returns result as numeric value. Values of extern variables are passed in vals parameter, the number of values is passed in num value.
function Compute(num : integer = 0; vals : PCalcArray = nil) : string; - compute previously compiled program and returns result as string value.
function ComputeStrN(instr : string; num : integer = 0; vars : PStringArray = nil; vals : PCalcArray = nil) : double; - compiles and computes source string. Returns result as numeric value.
function ComputeStr(instr : string; num : integer = 0; vars : PStringArray = nil; vals : PCalcArray = nil) : string; - compiles and computes source string. Returns result as string value.
procedure StartGetVars(n : integer); - starts new parameters evaluation for variable argument list function (see Section creating user functions and modules).
function GetNextVar(var x : TCalcItem) : boolean; - evaluates next variable argument list function parameter. Returns false for last value (see Section creating user functions and modules).
function GetLocalString(name : string; defvalue : string) : string; - if TArtFormula compiled with multilang support, this function returns local value from current loaded language for string name and if string not set or empty it returns defvalue.
procedure LoadLang(lngfile : string); - if TArtFormula compiled with multilang support, this procedure load local strings from lngfile.
procedure SetLocalString(name : string; value : string); - if TArtFormula compiled with multilang support, this procedure can be used to change error strings in runtime.
procedure CompileProgram(instr : string); - Compile source text as the set of subroutines with global vars and constants definition.
function RunSub(name : string;vals : PCalcArray = nil) : string; - Run subroutine by name with optional argument list;
function RunSub(idx:integer; vals : PCalcArray = nil) : string; - Run subroutine by index with optional argument list;
Events:
property GetVarsCount : TArtFormulaGetVarsCountProc;
TArtFormulaGetVarsCountProc = procedure(Vname:string; var count:integer; wantnumber:boolean=false) of object;
If you set ExternGetVars all unknown identifiers will treated as external variables. To evaluate them you should implement GetVarsCount and GetVarValue event handlers. GetVarsCount should return in count parameter number of values associated with Vname variable. Value of wantnumber specifies whether ArtFormula expects numerical values. GetVarsCount is called by ArtFormula when you pass external variable as a parameter of variant argument list function.
property GetVarValue : TArtFormulaGetVarProc;
TArtFormulaGetVarProc = procedure(Vname:string; n : integer; var Val:string; wantnumber:boolean=false) of object;
GetVarValue should return in Val parameter n-th value associated with Vname variable. n will change from 0 to count-1 where count returned by GetVarsCount event. Value of wantnumber specifies whether ArtFormula expects numerical values.
Note. Using GetVarsCount and GetVarValue you can implement for example range calculation in spreadsheet application when one parameter is passed in variant argument list function associated with number of cells.
Note. For fixed argument list function and when external variables are used as operands of expression, ArtFormula don't calls GetVarsCount and evaluates only first value associated with variable.
property OnStep : TArtFormulaStepProc
TArtFormulaStepProc = procedure(AF:TArtFormula) of object;
Write OnStep event handler to debug your ArtFormula program (see demo).
property OnLoadLang : TArtFormulaLoadLangProc;
TArtFormulaLoadLangProc = procedure of object;
If TArtFormula compiled with multilang support, this event arises when new local strings loaded from lng file. You can use this event to change local strings for installed TArtFormula modules or user functions.
Write ArtFormula program (see Section ArtFormula Syntax). Prepare string array with external variable names.
Note. If you set ExternGetVars all unknown identifiers will treated as external variables. To evaluate them you should implement GetVarsCount and GetVarValue event handlers.
Prepare TCalcArray with variable values. Use setN and setS procedures.
TFormulaDataType = (fdtnumber, fdtstring, fdtgetvar); TCalcItem = record data : double; str : string; typ : TFormulaDataType; end; TCalcArray = array of TCalcItem; PCalcArray = ^TCalcArray;
Use AddUserConstant, AddUserFunction and AddModuleFunction procedures to add constants, user and module functions.
Call Test and/or Compile procedures passing source code as instr parameter, number of external variables in num parameter and pointer to array of variable names in vars parameter. If there no errors, call Compute or ComputeN (depending on desirable result type) procedure, passing number of values in num parameter and pointer to variable values in vals parameter.
To compile and compute expression by one call use ComputeStrN or ComputeStr procedures.
See File module (af_file.pas in demo folder) as example of module developing.
With CompileProgram you can compile source code as the set of subroutines. Use RunSub to call one of precompiled subroutines then.
If ArtFormula compiled with {$define AF_MULTILANG} directive it can use localization lng files to localize TAtFormula messages and error strings.
Lng file is any ini file with [ArtFormula] section, contained pairs of StringName = LocalValue. You can find predefined StringNames in demo lng files inside Demo\Lang folder.
To load another lng file call LoadLang procedure passing lng file name as a parameter. Then you can use LocalStrings property and GetLocalString function to obtain local string values.
Also you can define your own StringNames in lng file to use them with user defined functions and modules. In this case you should write OnLoadLang even handler to change your local string values when new lng file has loaded.
TArtFormula has AddUserFunction and AddModuleFunction procedures allow you to add user defined functions and modules. AddUserFunction takes from three to seven parameters: function name, number of parameters, pointer to corresponded Delphi function, default argument values, flag whether function is main function of new module, flag allowing one to replace an existing function at runtime and name of default module function name.
First you should write Delphi function of next type:
function MyFunction(var Calc : TFormulaCalc):TCalcItem;
This function should return result of TCalcItem type. If function returns string value, set str member of Result variable and set typ member to fdtstring else set data member and typ to fdtnumber. Use setN and setS procedures to do it.
Function takes one parameter of TFormulaCalc type. TFormulaCalc is stack object used to implement arithmetical and logical calculation. It has many methods and properties but most useful are:
function TopN: double; - returns top value as number.
function TopS: String; - returns top value as string.
property ItemN[i:integer]:double; - returns/sets stack item value as number. ItemN[0] - top element, Item[1] - next and so on.
property ItemS[i:integer]:string; - returns/sets stack item value as string.
property Item[i:integer]:PCalcItem; - returns/sets pointer/value of stack item.
property Parent : TArtFormula; - pointer to parent ArtFormula object.
You should understand, that parameters for function are passed in stack. So last function parameter is the top (Item[0]) item in stack. For example if function has three parameters first parameter will be ItemN[2] or ItemS[2], second - ItemN[1] or ItemS[1], third - ItemN[0] or ItemS[0] (TopN or TopS).
If you want to create variable argument list functions, you should pass -1 as paramcount to AddUserFunction. For such functions ArtFormula pass the number of parameters as last argument (top in Calc stack). Variable argument list functions can take external variables evaluated via GetVarValue and GetVarsCount events (if ExternGetVar is true). Such external variables can return more than one value. It useful e.g. for spreadsheet application when function parameter can be the range of spreadsheet cells. Such external variables pass as one parameter in Calc stack of fdtgetvar type. StartGetVars and GetNextVar methods help you to deal with such parameters. Fist you should determine number of arguments passed via stack (call TopN), then pass this value into StartGetVars to begin evaluation of real parameters and then call GetNextVar until it return false. See implementation of min, max, sum and other function to understand this technique.
If it is needed you can define default function values, passed them in devfal parameter of PDefArray type. PDefArray is a pointer to array of TCalcItem. You should prepare such array and pass pointer as devfal parameter. The size of array should be equal to the count of function parameters. For parameters with default values you should set typ member of corresponded array element to fdtnumber or fdtstring value and set data or str to default value. If there is no default value for function parameter typ member of corresponded array element should be set to fdtgetvar. To call function with default value in ArtFormula program you can just omit any value before comma (if this parameter is the last one you can also omit comma), e.g.
-
formatdate(,now()) - use default value "c" as the first parameter.
-
msg("Message","Caption") - use default value for last parameter.
-
input(,'Input something:',) - use default value for first and last parameters.
If you have two user functions somename with no parameters and setsomename with one parameter, you can use these two functions as property:
-
somename - returns value
-
somename := val - sets value (equals to setsomename(val))
TArtFormula has special type of function called module. Module can have its own functions. Module can be considered as functions library or as object with methods and properties.
To create module, call AddUserFunction setting module parameter to true. It creates main function of module. AddUserFunction returns pointer to new allocated function table item which should be passed as first argument of AddModuleFunction when you add function to the module. The name of function passed to AddUserFunction becomes the name of the module. You can call main function of module in ArtFormula program as usual function. The call of other module function should be preceded by module name and period (.). If you pass arguments to main function of module, ArtFormula first calls main function. For example:
module('x').fun('y') - calls main function with argument 'x' and then function fun with argument 'y';
module.fun('y') - calls only function fun with argument 'y'.
The result of main function of module is passed as additional (first) argument of other module function. This feature allows one to use modules as objects. The common way to do so is to create internal table of objects, write module function which creates new entry in table and returns index. Main module function takes one parameter - index of object in internal table and simply returns it. This index is passed as first parameter to other module functions, so it is known with which object deal with.
For example:
$f := file.new; // creates new file object file($f).name := 'info'; file($f).open('read');
ArtFunction module can't have data members but you can easily create properties. To create property with name SomeName you should write two functions. First one with name SomeName without parameters to return property value and second one with name SetSomeName with one parameter to set property value. So when ArtFormula meets statement module.SomeName := Val it automatically compiles it as module.SetSomeName(Val).
Also you can create properties with parameters (as like array properties in Delphi). In this case you should write one function with variable number of arguments. Than ArtFormula will treat module.ParamProperty(val1,val2) := val3 as module.ParamProperty(val1,val2,val3). Your function should test number of parametars passed in function (returned by Calc.TopN). In this example if two parameters passed, function should return some property corresponded to val1 and val2. If three parameters passed, function should set some property corresponded to val1 and val2 to val3 value.
If you have ParamProperty - variable number of arguments function in your module, you can set this function as default. So in this case module.somename will be compiled as module.ParamProperty("somename") and module.somename := val will be compiled as module.ParamProperty("somename", val). It can be useful to create for example database modules to deal with data field: dbtable($t).somefield := val instead of dbtable($t).field("somefield", val).
You can easily write callback functions for your modules. For more information see Demo program.
Preamble
ArtFormula has no statements in common meaning. All program structure elements (branching, loops) are realized as function calls. So every statement has parameters and result value. For example pascal block statement (begin ... end) is realized as block(...) function, if statement is call of condition() function. TArtFormula component has predefined constant so you can use pascal like notation instead of function calls. Also you can use semicolon instead of comma. Below we will show both (function call and statement like) syntax for all programming abilities of ArtFormula.
Comments
Comments are ignored by TArtFormula compiler. There are two ways to construct comments: text between a left brace and a right brace any text between a double-slash and the end of the line.
{ Multi-line comment } //Single line comment
Types end Expressions
ArtFormula has the only data type. Depending on expression ArtFormula treats data value ether as numeric or string type. For comparison operation (=, <> ,>= , <= , >, <) and addition ArtFormula tries to convert operand first to numeric values and if this impossible to string. For example: "123" + 321 returns 444, but "x123" + 321 results in "x123321".
Numeric literals (integer and real) can be represented in common decimal notation. The dollar-sign prefix indicates a hexadecimal numeral. The first hexadecimal digit after $ must be one of 0..9 - for example $0AF (not $AF).
String value can be enclosed both in double and single quotes. Two sequential apostrophes or double quotes in a same quoted string denote a corresponded single character. To specify symbol with special code use # symbol followed by corresponded integer constant.
There is no boolean type. As in C language ArtFormula treats zero value as False and nonzero as True.
Date and time are represented as floating point value.
Operators
Numeric operators: +, - , * , /, % (or mod), \ (or div), ^ (raise to a power).
Comparison operators: =, <>, >, <, >=, <=.
Logical operators: & (or and), | (or or), xor, ! (or not).
Bitwise operators: && (or band), || (or bor), bxor, !! (or bnot), << (or shl), >> (or shr).
String operations: @ (concatenation), like (or ==). Like operator use DOS-like notation (? - any simbol, * - any sequence of symbols) and not RegExpr.
In contrast to addition @ always treats its operands as strings, i.e. "123" @ 321 results in "123321" (unlike it "123" + 321 returns 444).
Constants
You can define new constant in ArtFormula program with extended syntax (see Extended syntax section).
Also you can add constant by AddConstant procedure of TArtFormula component. Constant in ArtFormula is not the same as in pascal. It more close to C #define. If ArtFormula compiler meets constant, it replaces constant with corresponded value. So you can define constant not only to specify numeric or string values but also for token replacement.
For example you can define constant MyIf equals to If x=0 then return(0) endif and use MyIf as new statement.
Predefined functions
As ArtFormula descended from simple expression evaluation class, it has a wide number of predefined functions. Default values of function are given in brackets.
Numeric functions:
Trigonometric, hyperbolic and invert functions:
sin(x), cos(x), tan(x), asin(x), acos(x), atan(x), cosh(x), sinh(x), tanh(x), asinh(x), acosh(x), atanh(x)
Exponential and logarithmic functions:
log(x), lg(x) (log base 10), exp(x), sqrt(x)
Integer and factional part of number: int(x), frac(x)
round(x,[n=0]) - to round a given value to a specific number of decimal places.
trunc(x,[n=0]) - to truncate a given value to a specific number of decimal places.
Absolute value: abs(x)
Sign of a number: sign(x)
Random value and initialization of the random number generator: rnd, randomize
number(str) - tries to convert beginning of the string str into number or returns 0:
-
number(123) = 123
-
number("2e2") = 200
-
number("+835xyz") = 835
-
number("-a56") = 0
Statistical functions (all statistical functions take variable argument list):
max(x1,x2,...) - maximum of passed values,
min(x1,x2,...) - minimum of passed values,
avg(x1,x2,...) - average value,
stddev(x1,x2,...) - standard deviation (unbiased),
stddevp(x1,x2,...) - standard deviation (biased),
sum(x1,x2,...) - sum of passed values,
sumofsquares(x1,x2,…) - sum of passed values squares
count(x1,x2,...) - count of passed values
variance(x1,x2,...) - variance (unbiased)
variancep(x1,x2,...) - variance (biased)
Logical functions:
iff(e,x,[y=0]) - checks the expression passed as e and returns x if it evaluates to true, or y if it evaluates to false.
isnumber(x) - returns true if x is number.
exists('varname') - returns true if variable varname exists.
String functions:
chr(x) - returns character with code x.
length(s) - length of the string.
trim(s), trimleft(s), trimright(s) - trims leading and/or trailing spaces and control characters from a string
uppercase(s), lowercase(s) - converts string to upper(lower)case.
midstr(s,fron,len), leftstr(s,len), rightstr(s,len) - returns the substring of a specified length that appears at the specified position (start, end) of a string.
pos(t, s) - returns the index value of the first character of t that occurs in s.
code(s) - returns code of first character of string s.
stringofchar([c=" "], count) - returns a string with a specified number of repeating characters.
concat(s1,s2,...) - concatenates an arbitrary number of strings.
hex(s) - returns numeric value for hexadecimal string.
Date and Time functions:
date(s) - converts string to numeric date-time value.
now - returns the current date and time.
dayofweek(d) - returns the day of the week represented by a value d. Returns a value between 1 and 7, where 1 indicates Monday and 7 indicates Sunday.
year(d), month(d), day(d), hour(d), minute(d), second(d), millisecond(d) - returns corresponded part of date-time value d.
isleapyear(n) - indicates whether a specified year n is a leap year.
encodedate(year, month, day) - returns a date-time value that represents a specified year, month and day.
Formatting functions:
format(s,x) - formats a floating point value x using the format string given by s. Uses the same format string as format Delphi FormatFloat function.
formatf(s,x) - formats a floating point value x using the format string given by s. Uses the same format string as format Delphi Format function.
formatdate([s="c"],d) - formats a date-time value d using the format string given by s. Uses the same format string as format Delphi FormatDateTime function.
Dialog functions:
input([caption="Input value"], [prompt="Value"], [val=""]) - displays an input dialog box that lets the user enter a value.
msg(text, [caption="Message"], [flags=0]) - displays a specified message to the user. ArtFormula has predefined Windows MessageBox MB_ and ID constants.
User functions and Modules
ArtFormula provides subroutines in ArtFormula programs with extended syntax (see below). Also you can use AddUserFunction and AddModuleFunction procedures do add new functions and modules to ArtFormula. User functions are called in the same way as predefined functions. The call of module function should be preceded by module name and period (.). If module name followed by parenthesis with parameters, then ArtFormula calls main function of module first.
For example: module('x').fun('y') - calls main function of module with argument 'x' and then function fun with argument 'y'; module.fun('y') - calls only function fun with argument 'y'.
Variables, records and arrays
ArtFormula has three kinds of variables: global (see in Extended syntax section), external and internal. External variables are passed as argument of Compile procedure. Also if ExternGetVar is true, ArtFormula will consider all unknown identifiers as external variables. In this case you should implement GetVarsCount and GetVarValue event handlers. You can reference external variables by name.
Internal variables are defined in ArtFormula program. There are two functions to define variable:
define("name", value) and defines("name1", "name2", "name3", ...).
Instead of defines() you can use next syntax:
var "name1", "name2", "name3", … end.
If AutoCreateVars property is true ArtFormula will create new variable with the first assignment statement. So you no need to use define, defines or var statement. But if unknown variable is in the expression, ArtFormula will rise an expression.
To get/set value of variable use functions:
get("name") and set("name", value).
In place of get("name") you can use $name, and in place of set("name", value) - $name := value.
As set and get are functions, you can use an expression to specify variable name. Corresponded alternative syntax is $(expression).
ArtFormula has compound assignment operators:
-
$name += val equals to plusset("name", val)
-
$name -= val equals to minusset("name", val)
-
$name *= val equals to mulset("name", val)
-
$name /= val equals to divisionset("name", val)
-
$name \= val equals to divset("name", val)
-
$name %= val equals to modset("name", val)
-
$name @= val equals to concatset("name", val)
ArtFormula provide two special functions for variable incrementing and decrementing:
inc('name') and dec('name'). Instead of function call you can use next syntax: $name++ and $name--.
ArtFormula does not provide records or arrays, but can work with variables named 'n_1', 'n_2', 'n_3' ... as with array and with variables named 'n_a', 'n_b', 'n_c' ... as with associative array or record. To reference element of such "array" use expression $('arrayname_'@index) or $arrayname[index] for example $n[$i]. Two dimensional arrays should consist of variables 'x_1_1', 'x_1_2', 'x_2_1' and so on. To reference element use $x[$i,$j] or $x[$i][$j] (equal to $('x_'@$i@'_'@$j)). You can define multidimensional arrays in the same way.
Also you can use any strings as array indexes (like as in Perl hashes). For example: $hello['word']. But you should understand that $hello['word'] is the same variable name as $hello_word.
String indexes can be used as record fields. So $hello.word is legal synonym for $hello['word'] and $hello_word.
Example
begin $person[1].name := 'John'; $person[1].age := 25; $person[2].name := 'Karl'; $person[2].age := Input('Karl age?','>', 30); if $person[1].age > $person[2].age then print($person[1].name@' is older than '@$person[2].name); else print($person[2].name@' is older than '@$person[1].name); endifelse; end
Array functions:
There are several array functions:
-
array("name", val1, val2, val3, ... ) - creates array $name[1] = val1, $name[2] = val2, $name[3] = val3 ...
-
dim("name", N) - creates array $name[1] ... $name[N] with zero values
-
setarrayindex(idx) - sets array base index (0 or 1). Can be used in form arrayindex := val
-
arrayindex - returns current array base index
Statements
ArtFormula has five special functions treated as statements. In fact these functions are not real functions as compiled in special bytecode.
Return procedure:
return(value) - stops evaluation and return value as a result of computation.
Compound statement:
block(p1,p2,p3,…) - returns value of last argument.
Predefined constants:
begin = block(
end = )
Corresponded alternative syntax:
begin p1; p2; p3; … end
If statement
condition(expression, p1, p2) - returns p1 or p2 depending of expression value.
Corresponded alternative syntax:
if expression then p1 else p2 endif
if expression then p1 endif.
or (simple form):
if expression ; p1 ; p2 end
if expression ; p1 end.
Also you can use if statement as case statement (this form has no corresponding function):
if expression1 then p1 elseif expression2 then p2 elseif [...]
else pn endif.
Unlike the version of ArtFormula before 4.1, endif is now used in all cases to close any versions of if statement (if/then, if/then/else, if/then/enlseif, etc.). Endd and endifelse now are synonyms of endif. But if you use simple syntax of if statement you should close it with end as in previous version of ArtFormula.
Difference between condition and iff functions is that iff computes both its branches and condition only one. For example
iff(1,message('1','1',0),message('0','0',0)) shows two message boxes and
if 1; message('1','1',0); message('0','0',0) end shows only first message box.
While statement
loop(expression, p)
Predefined constants:
while = loop(
do = ,begin(
wend (and endwhile) = ))
Corresponded alternative syntax:
while expression do p endwhile
Repeat statement
till(p, expression)
Predefined constants:
repeat = till(begin(
until = ),
Corresponded alternative syntax:
repeat p until expression end
For statement
series(initialization, condition, increment, p)
Predefined constants:
for = series(
endfor = ))
Corresponded alternative syntax:
for initialization; condition; increment do p endfor
For statement in ArtFormula is equal to C iterative loop. For example:
for $i := 1; $i <= 10; $i++ do $x[$i] := $i; endfor
All loop functions return the number of iteration done.
Using ArtFormula
As all ArtFormula statements are functions call, you can use them in any expressions. For example next statements are correct for ArtFormula.
$a := $b := 2;
$a := begin $c:=100; while $c>10 ; $c-- end; $c-3 end;
for begin $a:=1; $c:=10 end; $a<10; begin $a++; $b++ end; $c--; end;
Extended syntax
With TArtFormula v2.x+ you can use subroutines in your program. In this case program consists of the set of declaration:
-
Constants
Const constname1 = val1 constname2 = val2 ...
- Global variables
Global varname1[ = val1], varname2[ = val2] ... ...
- Subroutines
Sub subname1(paramname1[ = defvalue1], paramname2[ = defvalue2], ...) begin ... end
To call one subroutine from other just write it's name with required parameters. If default values of subroutines defined you can omit them, e.c. msg('Text',,1) will show message box with default caption 'Message'.
Note that begin end block statement is optional in subroutine code as body of subroutine is just any valid formula.
You can use recursion in your subroutines. Also to make mutual recursion you should use forward declaration:
Forward [Sub] subname(paramname1[ = defvalue1], paramname2[ = defvalue2], ...); ... Sub subname() begin ... end
Note that subroutine should be fully declared in forward declaration and should not be any parameter declaration in later subroutione code. All forward declaration should by satisfied or you get an error.
Demo program comes with several modules:
-
ADO module to read/write databases with ADO (DBF module for Lazarus based on TDbf)
-
Calc module to read/write Excel files via Open Office Calc
-
Excel module to read/write Excel files via MS Excel
-
File module to read/write text files
-
Form module to create dialog forms with several types of controls including labels, buttons, edit boxes, check boxes, radio buttons, date pickers, string grids, combo boxes, memos, lists and checked lists
-
Ini module to read write ini files
-
List module is an example module that store name/value pairs in TStringList
Use Demo program and source code of modules to see how to use them in ArtFormula programs.
ArtFormula package includes another component TArtFormulaN. This component is forerunner of TArtFormula.
TArtFormulaN can only evaluate numeric expressions. It doesn't support strings, arrays, modules, scripting, but it much faster (about five times) then TArtFormula. So you may wish to use TArtFormulaN when you need quick numeric computation along with ability to specify expression in symbolic notation in runtime.
Also TArtFormulaN can symbolically evaluate derivatives of a function.
TArtFormulaN supports only the next functions:
sin, cos, tan, log (or ln),lg, exp, sqrt, int, frac, abs, atan, asin, acos, asinh,acosh,atanh,cosh,sinh,tanh, sign, rnd, max, min, avg, stddev, stddevp, sum, sumofsquares, count, variance, variancep, iff.
Unlike TArtFormula user defined functions for TArtFormulaN takes two parameters: (pos : integer; var data : array of double). Array represents a calculator stack and pos - position of stack top. So data[pos] is last parameter, data[pos-1] - next to last and so on. User defined function should returns result of double type.
To evaluates derivative TArtFormulaN has 3 functions:
function Diff(x : string):string; - to differentiate the compiled function with respect to x.
function DiffStr(instr : string; num : byte; vars : PStringArray; x : string):string; - to compile and differentiate function defined by instr.
function DiffStr(instr : string; vars : string; x : string):string; - to compile and differentiate function defined by instr where variables are defined by comma divided list vars.
First version of C++ class Formula (forerunner of TArtFormula) was written for Borland C++ compiler about 1997. Later it was ported to DJGPP environment and then to Borland C++ Builder. I've used Formula class for numeric research both in my master thesis (1999) and PhD dissertation (2002). About December 2004 I've decided to translate C++ Formula class to Delphi. Thus first version of pascal class TArtFormula was developed. Now this version is renamed to TArtFormulaN. In the middle of 2005 I've realized that I should extend ArtFormula syntax and add string handled functions to meet my requirements. So fist beta version of TArtFormula Delphi component was released.
- Version 4.2 Added CommaDS property.
-
Version 4.1 New syntax for IF statement with optional ELSEIF branches. ENDIF now closed any version of IF statement (except simple syntax: IF expr; true; false END; ). Fixed some bugs.
-
Version 4.0 Added functions/user function properties, compound assignment operators, record-like variable names, parameter properties and default functions in modules, array functions (array, dim, arraybase property), number function. Fixed bugs with global variables. ItemN, ItemS and Item are now properties (proposed by Guilherme Wiethaus). Demo program comes with several useful modules (ADO/Dbf, Calc, Excel, Ini, Form, File).
-
Version 3.5 Added support for latest Delphi versions.
-
Version 3.4 Recursion and forward declaration.
-
Version 3.3 Default values for functions and subroutines.
-
Version 3.2 Fixed some bugs in module functions evaluation.
-
Version 3.1 Added AutoCreateVars property, small Win CE Lazarus Demo.
-
Version 3.0 Added runtime line numbering, debugging, and SetLocalString function (proposed and written by Alan Bell bellsys[a]gmail.com).
-
Version 2.2: Added flag in AddUserFunction and AddModuleFunction to replace existed functions (including predefined) in runtime. Callback functions for modules. Changed error strings to show variable, constant and function names. Fixed some bugs in TArtFormulaN component. Many thanks to Alan Bell (bellsys[a]gmail.com).
-
Version 2.1.3: Fixed some bugs in TArtFormulaN component.
-
Version 2.1.2: Fixed some errors in TArtFormulaN component. Added support for ARM processors with Big Endianess. (Both thanks to Bogdan Vuk bogdan.vuk[a]siol.net)
-
Version 2.1.1: Little update. Fixed some errors in TArtFormulaN component.
-
Version 2.1: Improved Delphi XE+ compotability. Added hex, trunc and round functions. New Lazarus Demo.
-
Version 2.0: Added CompileProgram and RunSub procedures to treat source text as the set of subroutines with global vars and constants definition.
-
Version 1.8: Delphi XE compotability added
-
Version 1.7: Submodules (embedded modules of the form mainmodule.submodule.function), removed memory leak (thanks to Fabian Cios), improved spreadsheet calculation (thanks to Martin Stastny mstastny[a]vision.cz)
-
Version 1.6: Added LIKE operator
-
Version 1.5.1: Added ModuleName, FunctionName and ParamCount properties. (suggested by Jonas Jasas Jr. (jon[a]stekas.lt))
-
Version 1.5: Added Lazarus support. Improved Delphi 2010 compatibility (thanks to slq.mail[a]163.com). Now ArtFormula will return changed values of external variables. Added ZeroEmptyString property
-
Version 1.4.1: Bug in spreadsheet calculation (reported by Martin Stastny) has been fixed.
-
Version 1.4: Added multlang support (thanks to Fabian Cios (servifabian[a]hotmail.com) for idea and Spanish lang). Fixed some bugs.
-
Version 1.3: Added derivatives evaluation. Fixed bug with external variables. Added bitwise operations.
-
Version 1.2: Fixed some bugs. Added Delphi 2009 support.
-
Version 1.1: Added ExternGetValue mechanism, concat function. Iff function now can return string result.
-
Version 1.0b: First working version