ArtFormula v1.5 for Delphi and Lazarus
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.
TArtFormula description
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; - byte code 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.
Published properties:
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 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 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 leadin zero before point in floationg 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 az 0 in arithmetic operations.
Methods:
procedure SetVar(name : string; value : PCalcItem); - sets value of variable, given by name parameter.
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; ismodule: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):PTableItem; - adds module function (see Section creating user functions and modules).
function Test(instr : string; num : byte = 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 : byte = 0; vars : PStringArray = nil) : string; - compiles source string and returns compiled byte code. On error raises exception.
function ComputeN(num : byte = 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 : byte = 0; vals : PCalcArray = nil) : string; - compute previously compiled program and returns result as string value.
function ComputeStrN(instr : string; num : byte = 0; vars : PStringArray = nil; vals : PCalcArray = nil) : double; - compiles and computes source string. Returns result as numeric value.
function ComputeStr(instr : string; num : byte = 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.
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 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.
How to use
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.
Multilang support
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.
Creating user functions and modules
TArtFormula has AddUserFunction and AddModuleFunction procedures allow you to add user defined functions and modules. AddUserFunction takes three or four parameters: function name, number of parameters, pointer to corresponded Delphi function and flag whether function is main function of new module.
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.
function ItemN(i:integer):double; - returns stack item value as number. ItemN(0) - top element, Item(1) - next and so on.
function ItemS(i:integer):string; - returns stack item value as string.
function Item(i:integer):PCalcItem; - returns pointer to 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.
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).
ArtFormula Syntax
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 constitutes a comment. Any text between a double-slash and the end of the line constitutes a 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 ASCII 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).
Concatenation operator: @.
Difference in addition and concatenation operations is that @ always treats its operands as strings, i.e. "123" @ 321 results in "123321" (unlike it "123" + 321 returns 444).
Constants
You can't define new constant in ArtFormula program. But 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.
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 logarithmical functions:
log(x), lg(x) (log base 10), exp(x), sqrt(x)
Integer and factional part of number: int, frac
Absolute value: abs
Sign of a number: sign
Random value and initialization of the random number generator: rnd(x),
randomize
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) - 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.
String functions:
chr(x) - returns character
with ASCII 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 an ASCII 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 then returns ASCII 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.
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,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, prompt, val)
- displays an input dialog box that lets the user enter a value.
msg(text, caption, flags) - displays a specified message to the user.
User functions and Modules
Present version of ArtFormula do not provides subroutines in ArtFormula programs. But 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 and arrays
ArtFormula has two kinds of variables 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.
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 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 arrays, but can work with variables named 'n1', 'n2', 'n3' ... as with array. To reference element of such "array" use expression $('arrayname'@index) or $arrayname[index] for example $n[$i]. Two dimensional arrays should consist of variables 'x1_1', 'x1_2', 'x2_1' and so on. To reference element: $x[$i,$j] (equal to $('x'@$i@'_'@$j)). You can define multidimensional arrays in the same way.
Statements
ArtFormula has five special functions treated as statements. In fact these functions are not real functions as compiled in special byte code.
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.
Predefined constants:
if = condition(
then = ,begin(
else = ),begin(
endd = ))
endif = ),0)
Corresponded alternative syntax:
if expression then p1 else p2 endd or
if expression then p1 endif.
Pay attention that in first case statement should ends with endd as in fact if expression then p1 else p2 endd is equal to condition(expression, block(p1), block(p2)).
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 = ))
Corresponded alternative syntax:
while expression do p wend.
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(
next = ))
Corresponded alternative syntax:
for initialization; condition; increment do p next
For statement in ArtFormula is equal to C iterative loop. For example:
for $i := 1; $i <= 10; $i++ do $x[$i] := $i; next
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;
TArtFormulaN
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.
History
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.
Author
Artem V. Parlyuk, e-mail:artsoft@nm.ru, http://artsoft.nm.ru
License and disclaimer agreement
IMPORTANT - READ CAREFULLY
This license and disclaimer statement constitutes a legal agreement ("License Agreement") between you (either as an individual or a single entity) and Artem Parlyuk (the "Author"), for this software product in this particular case TArtFormula Delphi package ("Software"), including any software, media, and accompanying on-line or printed documentation.
BY DOWNLOADING, INSTALLING, COPYING, OR OTHERWISE USING THE SOFTWARE, YOU AGREE TO BE BOUND BY ALL OF THE TERMS AND CONDITIONS OF THIS LICENSE AND DISCLAIMER AGREEMENT. If you do not agree with the terms and conditions of this agreement, you must promptly cease all use of the software and destroy all copies of this software and all of its component parts in your possession or under your control.
This Software is owned by Author and is protected by copyright law and international copyright treaty.
This Software is freeware. You are granted the permission to use Software in your own applications for private or commercial purposes, provided your software contains the copyright notice "TArtFormula Delphi package Copyright (c) by Artem Parlyuk" and link to the Author site (http://artsoft.nm.ru) and Author e-mail (mailto:artsoft@nm.ru) .
You can freely distribute copies of the main archive as long as no alterations are made to the contents and no charge is raised except a reasonable fee for distributing costs. You may not remove copyright notices from copies of the Software. You may not claim this Software as written by anyone but Author, Artem Parlyuk.
The author has taken all possible care to ensure the software is error-free, however the author disavows any potential liability arising from any use of the software. This software is provided "as is" and without any warranties expressed or implied, including, but not limited to, implied warranties of fitness for a particular purpose, and non-infringement. You expressly acknowledge and agree that use of the Software is at your sole risk.
In no event shall the author be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use this software or documentation, even if the author has been advised of the possibility of such damages.
Any feedback given to the Author will be treated as non-confidential. The Author may use any feedback free of charge without limitation.