Sign in Join
Wiki copytemporary

How To Use TFPExpressionParser: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
1-`TFPExpressionParser` allows to analyze and calculate [[expression|expressions]] such as `sin(x)*cos(2*x)` for any value of the [[Variable|variable]] `x`. Besides mathematical expressions it can also handle [[Boolean|boolean]] values, string formulas, [[TDateTime|date/time]] values etc. Even user-provided functions can be linked in.
1+`TFPExpressionParser` allows to analyze and calculate [[expression|expressions]] such as `sin(x)*cos(2*x)` for any value of the [[Variable|variable]] `x`. Besides mathematical expressions it can also handle [[Boolean|boolean]] values, string formulas, [[TDateTime|date/time]] values etc. Even user-provided functions can be linked in.
22
3-It belongs to [[FPC]] [[FCL|Free Component Library (FCL)]] and is implemented in the [[Unit|unit]] `fpexprpars.pp`, folder `(fpc_source_dir)/packages/fcl-base/src`. Just add `fpexprpars` to the [[Uses|uses]] clauses to get access to its functionality. See the file "fpexprpars.txt" (in `(fpc_source_dir)/packages/fcl-base/examples`) for a short documenation.
3+It belongs to [[FPC]] [[FCL|Free Component Library (FCL)]] and is implemented in the [[Unit|unit]] `fpexprpars.pp`, folder `(fpc_source_dir)/packages/fcl-base/src`. Just add `fpexprpars` to the [[Uses|uses]] clauses to get access to its functionality. See the file “fpexprpars.txt“ (in `(fpc_source_dir)/packages/fcl-base/examples`) for a short documenation.
44
55## Creating the parser
66
7-You apply the parser by creating an instance like this:
8-
9-``` pascal
7+You apply the parser by creating an instance like this:
8+```
109uses
11- fpexprpars;
12-
13-var
14- FParser: TFPExpressionParser;
15-begin
16- FParser := TFpExpressionParser.Create(self);
17- // ... do something (see below)
10+ fpexprpars;
11+
12+ var
13+ FParser: TFPExpressionParser;
14+ begin
15+ FParser := TFpExpressionParser.Create(self);
16+ // ... do something (see below)
1817```
1918
20-If this is called from a [[Method|method]] of a [[TForm|form]], "[[Self|self]]" points to the form. Since the parser inherits from `TComponent`, there is no need to destroy it explicitly since its owner, the form, will do it. On the other hand, it is also possible to create the parser from anywhere in a [[Program|program]] without a form or even [[Class|class]] being involved; in this case use [[Nil|nil]] as the owner of the parser, but don't forget to `.Free` the parser after its usage:
21-
22-``` pascal
19+If this is called from a [[Method|method]] of a [[TForm|form]], “[[Self|self]]” points to the form. Since the parser inherits from `TComponent`, there is no need to destroy it explicitly since its owner, the form, will do it. On the other hand, it is also possible to create the parser from anywhere in a [[Program|program]] without a form or even [[Class|class]] being involved; in this case use `[[Nil|nil]]` as the owner of the parser, but don't forget to `.Free` the parser after its usage:
20+```
2321uses
24- fpexprpars;
25-
26-var
27- FParser: TFPExpressionParser;
28-begin
29- FParser := TFPExpressionParser.Create(nil);
30- try
31- // ... do something (see below)
32- finally
33- FParser.Free;
34- end;
35-end;
22+ fpexprpars;
23+
24+ var
25+ FParser: TFPExpressionParser;
26+ begin
27+ FParser := TFPExpressionParser.Create(nil);
28+ try
29+ // ... do something (see below)
30+ finally
31+ FParser.Free;
32+ end;
33+ end;
3634```
3735
3836## Built-in categories
3937
40-The parser is designed in a very flexible way, but the default parser is quite dumb. You have to specify which kind of expressions it will accept. This is done by adding the corresponding [[Identifier|identifier]] to the set of built-in categories. They are accessible by the parser's [[Property|property]] `BuiltIns`:
41-
42-``` pascal
38+The parser is designed in a very flexible way, but the default parser is quite dumb. You have to specify which kind of expressions it will accept. This is done by adding the corresponding [[Identifier|identifier]] to the set of built-in categories. They are accessible by the parser's [[Property|property]] `BuiltIns`:
39+```
4340type
44- TBuiltInCategory = (bcStrings, bcDateTime, bcMath, bcBoolean, bcConversion, bcData, bcVaria, bcUser, bcAggregate);
45- TBuiltInCategories = set of TBuiltInCategory;
41+ TBuiltInCategory = (bcStrings, bcDateTime, bcMath, bcBoolean, bcConversion, bcData, bcVaria, bcUser, bcAggregate);
42+ TBuiltInCategories = set of TBuiltInCategory;
4643```
4744
48-Here is a collection of the built-in symbols which can be used by adding categories to the parser's `BuiltIns` - it should be clear to anybody who "speaks" [[Pascal]] what these symbols mean...
49-
50-- **bcStrings**: `Length`, `Copy`, `Delete`, `Pos`, `Lowercase`, `Uppercase`, `StringReplace`, `CompareText`
51-- **bcDateTime**: `Date`, `Time`, `Now`, `DayOfWeek`, `ExtractYear`, `ExtractMonth`, `ExtractDay`, `ExtractHour`, `ExtractMin`, `ExtractSec`, `Extractmsec`, `EncodeDate`, `EncodeTime`, `ShortDayName`, `ShortMonthName`, `LongDayName`, `LongMonthName`
52-- **bcMath**: `cos`, `sin`, `arctan`, `abs`, `sqr`, `sqrt`, `exp`, `ln`, `log`, `frac`, `int`, `round`, `trunc`,
53-- **bcBoolean**: `shl`, `shr`, `IFS`, `IFF`, `IFD`, `IFI` (The `IFxxx` symbols have the same effect as fpc's `IfThen` for string (`IFS`), floating point (`IFF`), date/time (`IFD`), or integer (`IFI`) variables)
54-- **bcConversion**: `IntToStr`, `StrToInt`, `StrToIntDef`, `FloatToStr`, `StrToFloat`, `StrToFloatDef`, `BoolToStr`, `StrToBool`, `StrToBoolDef`, `DateToStr`, `TimeToStr`, `StrToDate`, `StrToDateDef`, `StrToTime`, `StrToTimeDef`, `StrToDateTime`, `StrToDateTimeDef`
55-- **bcAggregate**: `count`, `sum`, `avg`, `min`, `max`
45+Here is a collection of the built-in symbols which can be used by adding categories to the parser's `BuiltIns` \- it should be clear to anybody who “speaks” [[Pascal]] what these symbols mean...
5646
57-`bcData`, `bcVaria`, and `bcUser` are not used anywhere within `fpexprpars`. The last section gives instructions how to extend the parser with more functions.
47+ * **bcStrings** : `Length`, `Copy`, `Delete`, `Pos`, `Lowercase`, `Uppercase`, `StringReplace`, `CompareText`
48+ * **bcDateTime** : `Date`, `Time`, `Now`, `DayOfWeek`, `ExtractYear`, `ExtractMonth`, `ExtractDay`, `ExtractHour`, `ExtractMin`, `ExtractSec`, `Extractmsec`, `EncodeDate`, `EncodeTime`, `ShortDayName`, `ShortMonthName`, `LongDayName`, `LongMonthName`
49+ * **bcMath** : `cos`, `sin`, `arctan`, `abs`, `sqr`, `sqrt`, `exp`, `ln`, `log`, `frac`, `int`, `round`, `trunc`,
50+ * **bcBoolean** : `shl`, `shr`, `IFS`, `IFF`, `IFD`, `IFI` (The `IFxxx` symbols have the same effect as fpc's `IfThen` for string (`IFS`), floating point (`IFF`), date/time (`IFD`), or integer (`IFI`) variables)
51+ * **bcConversion** : `IntToStr`, `StrToInt`, `StrToIntDef`, `FloatToStr`, `StrToFloat`, `StrToFloatDef`, `BoolToStr`, `StrToBool`, `StrToBoolDef`, `DateToStr`, `TimeToStr`, `StrToDate`, `StrToDateDef`, `StrToTime`, `StrToTimeDef`, `StrToDateTime`, `StrToDateTimeDef`
52+ * **bcAggregate** : `count`, `sum`, `avg`, `min`, `max`
5853
59-> **Note:** These symbols are not [[case-sensitive]].
54+`bcData`, `bcVaria`, and `bcUser` are not used anywhere within `fpexprpars`. The last section gives instructions how to extend the parser with more functions.
6055
61-In order to use a mathematical expression the option `bcMath` has to be added to the parser's `Builtins`:
56+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Note:** These symbols are not [[case-sensitive]].
6257
63-``` pascal
64- FParser.Builtins := [bcMath]; // or FParser.Builtins := FParser.Builtins + [bcMath];
58+In order to use a mathematical expression the option `bcMath` has to be added to the parser's `Builtins`:
6559```
60+FParser.Builtins := [bcMath]; // or FParser.Builtins := FParser.Builtins + [bcMath];
61+```
6662
6763## Expressions
6864
6965### An expression with constants
7066
71-As a first example we have the parser calculate a very simple expression `'1+1'`.
72-
73-The first step is to tell the parser which expression is to be calculated. There is a property `Expression` for this purpose:
67+As a first example we have the parser calculate a very simple expression `'1+1'`.
7468
75-``` pascal
76- FParser.Expression := '1+1';
69+The first step is to tell the parser which expression is to be calculated. There is a property `Expression` for this purpose:
7770```
78-
79-The next step is to calculate the expression: just call `Evaluate` or `EvaluateExpression` - the former is is a [[Function|function]] while the latter one is a [[Procedure|procedure]] which passes the result as a parameter.
71+FParser.Expression := '1+1';
72+```
8073
81-``` pascal
74+The next step is to calculate the expression: just call `Evaluate` or `EvaluateExpression` \- the former is is a [[Function|function]] while the latter one is a [[Procedure|procedure]] which passes the result as a parameter.
75+```
8276var
83- parserResult: TFPExpressionResult;
84-begin
85- ....
86- parserResult := FParser.Evaluate; // or: FParser.EvaluateExpression(parserResult);
77+ parserResult: TFPExpressionResult;
78+ begin
79+ ....
80+ parserResult := FParser.Evaluate; // or: FParser.EvaluateExpression(parserResult);
8781```
8882
89-What is that mysterious `TFPExpressionResult`? Since the parser is very flexible and can deal with numbers, strings, date/times or booleans there must be a more complex data type which returns a calculation result:
90-
91-``` pascal
83+What is that mysterious `TFPExpressionResult`? Since the parser is very flexible and can deal with numbers, strings, date/times or booleans there must be a more complex data type which returns a calculation result:
84+```
9285type
93- TResultType = (rtBoolean, rtInteger, rtFloat, tDateTime, rtString);
94-
95- TFPExpressionResult = record
96- ResString : String;
97- Case ResultType : TResultType of
98- rtBoolean : (ResBoolean : Boolean);
99- rtInteger : (ResInteger : Int64);
100- rtFloat : (ResFloat : TExprFloat);
101- rtDateTime : (ResDateTime : TDatetime);
102- rtString : ();
103- end;
86+ TResultType = (rtBoolean, rtInteger, rtFloat, tDateTime, rtString);
87+
88+ TFPExpressionResult = record
89+ ResString : String;
90+ Case ResultType : TResultType of
91+ rtBoolean : (ResBoolean : Boolean);
92+ rtInteger : (ResInteger : Int64);
93+ rtFloat : (ResFloat : TExprFloat);
94+ rtDateTime : (ResDateTime : TDatetime);
95+ rtString : ();
96+ end;
10497```
10598
106-The member `ResultType` signals which one of the data fields is valid. It is important to understand this since the expression parser is very strict on [[Data type|data types]].
107-
108-In our example, we are adding two [[Integer|integers]], therefore the result is an integer as well. If, on the other had, we had used the expression `'1.0 + 1'`, the first number would have been a floating point value, and the result would have been a float! Therefore, always have a look at the member `ResultType` of the `TFPExpressionResult` before picking the result. To simplify the usage of the expression result data type, `fpexprpars` exposes a function `ArgToFloat` which gets the entire expression result [[Record|record]] as a parameter and selects the right component if a floating point result is expected:
99+The member `ResultType` signals which one of the data fields is valid. It is important to understand this since the expression parser is very strict on [[Data type|data types]].
109100
110-``` pascal
101+In our example, we are adding two [[Integer|integers]], therefore the result is an integer as well. If, on the other had, we had used the expression `'1.0 + 1'`, the first number would have been a floating point value, and the result would have been a float! Therefore, always have a look at the member `ResultType` of the `TFPExpressionResult` before picking the result. To simplify the usage of the expression result data type, `fpexprpars` exposes a function `ArgToFloat` which gets the entire expression result [[Record|record]] as a parameter and selects the right component if a floating point result is expected:
102+```
111103var
112- parserResult: TFPExpressionResult;
113- resultValue: Double;
114-...
115- parserResult := FParser.Evaluate; // or: FParser.EvaluateExpression(parserResult);
116- resultValue := ArgToFloat(parserResult);
104+ parserResult: TFPExpressionResult;
105+ resultValue: Double;
106+ ...
107+ parserResult := FParser.Evaluate; // or: FParser.EvaluateExpression(parserResult);
108+ resultValue := ArgToFloat(parserResult);
117109```
118110
119-> **Note:** Floating point constants in expressions must have a [[period|point]] as decimal separator, not a [[Comma|comma]] as used in some European countries. If your expression string comes from user input and contains decimal commas you have to replace the commas by points first before assigning it to the parsers's `Expression`.
111+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Note:** Floating point constants in expressions must have a [[period|point]] as decimal separator, not a [[Comma|comma]] as used in some European countries. If your expression string comes from user input and contains decimal commas you have to replace the commas by points first before assigning it to the parsers's `Expression`.
120112
121113### An expression with a variable
122114
123-In this example, we calculate the value of `sin(x)*cos(2*x)` for `x = 0.5`.
115+In this example, we calculate the value of `sin(x)*cos(2*x)` for `x = 0.5`.
124116
125117#### Defining variables
126118
127-At first we have to define the variables. We have only one, `x`. The parser has a [[Method|method]] `AddFloatVariable` to declare a floating point variable; there are also methods
128-
129-- `AddBooleanVariable`
130-- `AddStringVariable`
131-- `AddDateTimeVariable`
119+At first we have to define the variables. We have only one, `x`. The parser has a [[Method|method]] `AddFloatVariable` to declare a floating point variable; there are also methods
132120
133-for boolean, string and date/time variables, respectively.
121+ * `AddBooleanVariable`
122+ * `AddStringVariable`
123+ * `AddDateTimeVariable`
134124
135-Each one of these methods expects the name of the variable along with its default value. For our sample function we just call:
125+for boolean, string and date/time variables, respectively.
136126
137-``` pascal
138- FParser.Identifiers.AddFloatVariable('x', 0.5);
127+Each one of these methods expects the name of the variable along with its default value. For our sample function we just call:
139128```
129+FParser.Identifiers.AddFloatVariable('x', 0.5);
130+```
140131
141-`0.5` is entered here as a default value because that is the argument at which we want to calculate the expression (it will be shown below how to modify a variable). From now on, the parser will use this number whenever it finds the variable `x` in the expression.
132+`0.5` is entered here as a default value because that is the argument at which we want to calculate the expression (it will be shown below how to modify a variable). From now on, the parser will use this number whenever it finds the variable `x` in the expression.
142133
143-Of course, you can add other names, e.g. [[Constant|constants]] like `e`, etc. (The number [[Pi|pi]] is already built-in).
134+Of course, you can add other names, e.g. [[Constant|constants]] like `e`, etc. (The number [[Pi|`pi`]] is already built-in).
144135
145136#### Defining the expression
146137
147-In the next step, the expression string has to be passed to the parser:
148-
149-``` pascal
150- FParser.Expression := 'sin(x)*cos(2*x)';
138+In the next step, the expression string has to be passed to the parser:
151139```
140+FParser.Expression := 'sin(x)*cos(2*x)';
141+```
152142
153-It is essential to call this after setting up of the variables because the parser needs to know the variables for analyzing the expression.
143+It is essential to call this after setting up of the variables because the parser needs to know the variables for analyzing the expression.
154144
155145#### Calculating the expression
156146
157-This is done as before with the constant expression - here is a complete procedure which shows the equation and its result in a [[Dialog Examples|message box]]:
158-
159-``` pascal
147+This is done as before with the constant expression - here is a complete procedure which shows the equation and its result in a [[Dialog Examples|message box]]:
148+```
160149var
161- FParser: TFPExpressionParser;
162- resultValue: Double;
163-begin
164- FParser := TFPExpressionParser.Create(nil);
165- try
166- FParser.BuiltIns := [bcMath];
167- FParser.Identifiers.AddFloatVariable('x', 0.5);
168- FParser.Expression := 'sin(x)*cos(2*x)';
169- resultValue := FParser.Evaluate.ResFloat; // or: resultValue := ArgToFloat(FParser.Evaluate);
170- ShowMessage(FParser.Expression + ' = ' + FloatToStr(resultValue));
171- finally
172- FParser.Free;
173- end;
174-end;
150+ FParser: TFPExpressionParser;
151+ resultValue: Double;
152+ begin
153+ FParser := TFPExpressionParser.Create(nil);
154+ try
155+ FParser.BuiltIns := [bcMath];
156+ FParser.Identifiers.AddFloatVariable('x', 0.5);
157+ FParser.Expression := 'sin(x)*cos(2*x)';
158+ resultValue := FParser.Evaluate.ResFloat; // or: resultValue := ArgToFloat(FParser.Evaluate);
159+ ShowMessage(FParser.Expression + ' = ' + FloatToStr(resultValue));
160+ finally
161+ FParser.Free;
162+ end;
163+ end;
175164```
176165
177166#### Changing variables
178167
179-So far, `x` always has the value 0.5 - it behaves like a constant, we could have used the expression `'sin(0.5)*cos(2*0.5)'` as well.
180-
181-To make it behave more like a "variable", we now calculate the test function for the `x` values between `-10` and `10` at integer steps.
168+So far, `x` always has the value 0.5 - it behaves like a constant, we could have used the expression `'sin(0.5)*cos(2*0.5)'` as well.
182169
183-The main question is: How to replace the value assigned to a variable? There are several possibilities - all of them require the internal variable `Identifier` (type `TFPExprIdentifierDef`) which exposes various ways to access variables and their properties:
170+To make it behave more like a “variable”, we now calculate the test function for the `x` values between `-10` and `10` at integer steps.
184171
185-- Use the return value of the `AddFloatVariable` function.
186-- Seek an identifier by calling `FindIdentifierByName` with the variable name as a parameter.
187-- Access the identifier from the `Identifiers` collection of the parser by using the known index of the variable: We had added `x` as the only variable, therefore, it must be at index 0.
172+The main question is: How to replace the value assigned to a variable? There are several possibilities - all of them require the internal variable `Identifier` (type `TFPExprIdentifierDef`) which exposes various ways to access variables and their properties:
188173
189-Once the `Identifier` is known, the value of the variable can be changed by accessing the property `AsFloat` (or `AsDateTime` etc. accordingly):
174+ * Use the return value of the `AddFloatVariable` function.
175+ * Seek an identifier by calling `FindIdentifierByName` with the variable name as a parameter.
176+ * Access the identifier from the `Identifiers` collection of the parser by using the known index of the variable: We had added `x` as the only variable, therefore, it must be at index 0.
190177
191-``` pascal
178+Once the `Identifier` is known, the value of the variable can be changed by accessing the property `AsFloat` (or `AsDateTime` etc. accordingly):
179+```
192180var
193- FParser: TFPExpressionParser;
194- argument: TFPExprIdentifierDef;
195- s: string;
196- x: integer;
197- f: double;
198-begin
199- s:='';
200- FParser := TFPExpressionParser.Create(nil);
201- try
202- // Enable the use of mathematical expressions
203- FParser.BuiltIns := [bcMath];
204-
205- // Add the function argument
206- argument := FParser.Identifiers.AddFloatVariable('x', 0.0);
207-
208- // Define the function, using the argument name x as defined above
209- FParser.Expression := 'sin(x)*cos(2*x)';
210-
211- // Calculate some function values
212- for x := -10 to 10 do
181+ FParser: TFPExpressionParser;
182+ argument: TFPExprIdentifierDef;
183+ s: string;
184+ x: integer;
185+ f: double;
213186 begin
214- argument.AsFloat := x; // Set the function argument value
215- f := FParser.Evaluate.ResFloat; // Calculate the function value
216- s := s + format('x[%d]:[%g]' + LineEnding, [x,f]); // Demo output to display the result
187+ s:='';
188+ FParser := TFPExpressionParser.Create(nil);
189+ try
190+ // Enable the use of mathematical expressions
191+ FParser.BuiltIns := [bcMath];
192+
193+ // Add the function argument
194+ argument := FParser.Identifiers.AddFloatVariable('x', 0.0);
195+
196+ // Define the function, using the argument name x as defined above
197+ FParser.Expression := 'sin(x)*cos(2*x)';
198+
199+ // Calculate some function values
200+ for x := -10 to 10 do
201+ begin
202+ argument.AsFloat := x; // Set the function argument value
203+ f := FParser.Evaluate.ResFloat; // Calculate the function value
204+ s := s + format('x[%d]:[%g]' + LineEnding, [x,f]); // Demo output to display the result
205+ end;
206+
207+ // Show the result
208+ ShowMessage(s);
209+
210+ finally
211+ FParser.Free;
212+ end;
217213 end;
218-
219- // Show the result
220- ShowMessage(s);
221-
222- finally
223- FParser.Free;
224- end;
225-end;
226214```
227215
228216### Operators
229217
230-The expressions discussed above show that standard operators `+`, `-`, `*`, `/`, `<`, `<=`, etc., can be used to setup an expression. Additionally, in recent FPC versions, the operator `^` is available for calculating the power, and `mod` for the remainder of integer divisions (modulo).
218+The expressions discussed above show that standard operators `+`, `-`, `*`, `/`, `<`, `<=`, etc., can be used to setup an expression. Additionally, in recent FPC versions, the operator `^` is available for calculating the power, and `mod` for the remainder of integer divisions (modulo).
231219
232220### Aggregate functions
233221
234-In addition to "normal" function the parser supports also "aggregate" functions which are repeatedly applied to a variable, e.g. `sum(x)` which adds up all the values assigned to the variable `x`. These functions are available:
235-
236-- `min(x)` - Calculate the minimum of all values assigned to `x`
237-- `max(x)` - Calculate the maximum of all values assigned to `x`
238-- `sum(x)` - Calculate the sum of all values assigned to `x`
239-- `avg(x)` - Calculate the average of the values assigned to `x`
240-- `count(x)` - Count the numbers assigned to `x`
222+In addition to “normal” function the parser supports also “aggregate” functions which are repeatedly applied to a variable, e.g. `sum(x)` which adds up all the values assigned to the variable `x`. These functions are available:
241223
242-In order to have aggregate functions available the category `bcAggregate` must be added to the `BuiltIns` of the parser. Next step is to define a variable to which the aggregate function will be applied. The aggregation process must be initialized by calling the parser method `InitAggregate`; and whenever the variable value is changed the method `UpdateAggregate` must be called.
224+ * `min(x)` \- Calculate the minimum of all values assigned to `x`
225+ * `max(x)` \- Calculate the maximum of all values assigned to `x`
226+ * `sum(x)` \- Calculate the sum of all values assigned to `x`
227+ * `avg(x)` \- Calculate the average of the values assigned to `x`
228+ * `count(x)` \- Count the numbers assigned to `x`
243229
244-Here is an example which calculates the sum of the elements of an array:
230+In order to have aggregate functions available the category `bcAggregate` must be added to the `BuiltIns` of the parser. Next step is to define a variable to which the aggregate function will be applied. The aggregation process must be initialized by calling the parser method `InitAggregate`; and whenever the variable value is changed the method `UpdateAggregate` must be called.
245231
246-``` pascal
232+Here is an example which calculates the sum of the elements of an array:
233+```
247234uses
248- fpexprpars;
249-
250-const
251- Data: array[0..5] of Double = (1.221, 3.4, -1.0, -0.221, -3.0, -0.4);
252-var
253- FParser: TFPExpressionParser;
254- xVar: TFPExprIdentifierDef;
255- x: Double;
256-begin
257- FParser := TFPExpressionParser.Create(nil);
258- try
259- FParser.BuiltIns := [bcAggregate];
260- xVar := FParser.Identifiers.AddFloatVariable('x', 0);
261- FParser.Expression := 'sum(x)';
262- FParser.InitAggregate;
263- WriteLn('Sum of');
264- for x in Data do
235+ fpexprpars;
236+
237+ const
238+ Data: array[0..5] of Double = (1.221, 3.4, -1.0, -0.221, -3.0, -0.4);
239+ var
240+ FParser: TFPExpressionParser;
241+ xVar: TFPExprIdentifierDef;
242+ x: Double;
265243 begin
266- xVar.AsFloat := x;
267- WriteLn(xVar.AsFloat:10:3);
268- FParser.UpdateAggregate;
269- end;
270- WriteLn('----------');
271- WriteLn(FParser.Evaluate.ResFloat:10:3);
272- finally
273- FParser.Free;
274- end;
275-
276- ReadLn;
277-end.
244+ FParser := TFPExpressionParser.Create(nil);
245+ try
246+ FParser.BuiltIns := [bcAggregate];
247+ xVar := FParser.Identifiers.AddFloatVariable('x', 0);
248+ FParser.Expression := 'sum(x)';
249+ FParser.InitAggregate;
250+ WriteLn('Sum of');
251+ for x in Data do
252+ begin
253+ xVar.AsFloat := x;
254+ WriteLn(xVar.AsFloat:10:3);
255+ FParser.UpdateAggregate;
256+ end;
257+ WriteLn('----------');
258+ WriteLn(FParser.Evaluate.ResFloat:10:3);
259+ finally
260+ FParser.Free;
261+ end;
262+
263+ ReadLn;
264+ end.
278265```
279266
280267## Adding user-defined functions
281268
282-The default parser only knows the built-in functions mentioned above. One of the strengths of of the expression parser is that it is very easy to extend to include other functions. This can be done by calling the method `Identifiers.AddFunction`, e.g.
283-
284-``` pascal
285- FParser.Identifiers.AddFunction('tan', 'F', 'F', @ExprTan);
269+The default parser only knows the built-in functions mentioned above. One of the strengths of of the expression parser is that it is very easy to extend to include other functions. This can be done by calling the method `Identifiers.AddFunction`, e.g.
286270```
287-
288-In this example, we add the function `tan(x)` by specifying its name as it will be called in the function expressions (first parameter), the type of the result values (second parameter, "F" = float, "I" = integer, "D" = date/time, "S" = string, or "B" = boolean) and the type of the input values (third parameter, same logic). If a function accepts several input parameters the type of each one must be specified, e.g. by 'FF' for two floating point values, or 'FI' for a first float and a second integer parameter. The last parameter points to the function which is called whenever "tan" is found in the expression string. Since this function has a particular syntax we have to implement it in our own source code:
271+FParser.Identifiers.AddFunction('tan', 'F', 'F', @ExprTan);
272+```
289273
290-``` pascal
274+In this example, we add the function `tan(x)` by specifying its name as it will be called in the function expressions (first parameter), the type of the result values (second parameter, "F" = float, "I" = integer, "D" = date/time, "S" = string, or "B" = boolean) and the type of the input values (third parameter, same logic). If a function accepts several input parameters the type of each one must be specified, e.g. by 'FF' for two floating point values, or 'FI' for a first float and a second integer parameter. The last parameter points to the function which is called whenever “tan” is found in the expression string. Since this function has a particular syntax we have to implement it in our own source code:
275+```
291276procedure ExprTan(var Res: TFPExpressionResult; Const Args: TExprParameterArray);
292-var
293- x: Double;
294-begin
295- x := ArgToFloat(Args[0]);
296- Res.resFloat := tan(x);
297-end;
277+ var
278+ x: Double;
279+ begin
280+ x := ArgToFloat(Args[0]);
281+ Res.resFloat := tan(x);
282+ end;
298283```
299284
300-The result of the calculation is returned as parameter `Res` which is of the `TFPExpressionResult` type that we met above. The arguments for the calculation are passed by `Args` which is just an array of `TFPExpressionResult` values - again because parameters can have several data types. The word *`Result`* in `TFPExpressionResult` is maybe a bit misleading here, because this array holds all the *input* parameters as specified by the input types of the `AddFunction` method.
301-
302-When, in fpc 3.2 or later, the input parameter symbol string of the `AddFunction` method ends with a `'+'` then parameters with the previous type can be repeated as often as needed; this way a function with a variable parameter count can be implemented. In the following example, we add the function `SumOf` which adds up as many values as provided:
285+The result of the calculation is returned as parameter `Res` which is of the `TFPExpressionResult` type that we met above. The arguments for the calculation are passed by `Args` which is just an array of `TFPExpressionResult` values - again because parameters can have several data types. The word `_Result_` in `TFPExpressionResult` is maybe a bit misleading here, because this array holds all the *input* parameters as specified by the input types of the `AddFunction` method.
303286
304-``` Pascal
287+When, in fpc 3.2 or later, the input parameter symbol string of the `AddFunction` method ends with a `'+'` then parameters with the previous type can be repeated as often as needed; this way a function with a variable parameter count can be implemented. In the following example, we add the function `SumOf` which adds up as many values as provided:
288+```
305289procedure ExprSumOf(var Res: TFPExpressionResult; Const Args: TExprParameterArray);
306-var
307- sum: Double;
308- arg: TFPExpressionResult;
309-begin
310- sum := 0;
311- for arg in Args do
312- sum := sum + ArgToFloat(arg);
313- Res.ResFloat := sum;
314-end;
315-
316-begin
317- ...
318- FParser.Identifiers.AddFunction('SumOf', 'F', 'F+', @ExprSumOf);
319- FParser.Expression := 'SumOf(-1,2,3,4.1)'; // arbitrary number of arguments!
320- WriteLn(FParser.Evaluate.ResFloat:0:3);
321- ...
290+ var
291+ sum: Double;
292+ arg: TFPExpressionResult;
293+ begin
294+ sum := 0;
295+ for arg in Args do
296+ sum := sum + ArgToFloat(arg);
297+ Res.ResFloat := sum;
298+ end;
299+
300+ begin
301+ ...
302+ FParser.Identifiers.AddFunction('SumOf', 'F', 'F+', @ExprSumOf);
303+ FParser.Expression := 'SumOf(-1,2,3,4.1)'; // arbitrary number of arguments!
304+ WriteLn(FParser.Evaluate.ResFloat:0:3);
305+ ...
322306```
323307
324308## Some special code
325309
326310### Can I change the built-in function `sin(x)` to accept arguments in degrees, instead of radians?
327311
328-Yes. All built-in functions are collected in the instance `BuiltInIdentifiers` of class `TExprBuiltInManager`. Find the built-in identifier (`BuiltInIdentifiers.Find('sin')`, delete it and replace it by a user-defined function having the requested behavior. In FPC 3.2 or later, the identifier can be removed directly (`BuiltInIdentifiers.Remove('sin')`), in order versions the `BuiltInIdentifiers` must be cast to a descendant of `TExprBuiltInManager` in order to access the `Delete` method of its protected collection `Defs`:
329-
330-``` pascal
312+Yes. All built-in functions are collected in the instance `BuiltInIdentifiers` of class `TExprBuiltInManager`. Find the built-in identifier (`BuiltInIdentifiers.Find('sin')`, delete it and replace it by a user-defined function having the requested behavior. In FPC 3.2 or later, the identifier can be removed directly (`BuiltInIdentifiers.Remove('sin')`), in order versions the `BuiltInIdentifiers` must be cast to a descendant of `TExprBuiltInManager` in order to access the `Delete` method of its protected collection `Defs`:
313+```
331314uses
332- Math, fpExprPars;
333-
334-// these are needed only for fpc before v3.2
335-{
336-type
337- TMyBuiltinManager = class(TExprBuiltinManager);
338-var
339- idx: Integer;
340-}
341-
342-var
343- parser: TFPExpressionParser;
344-
345- Procedure ExprSin(Var Result: TFPExpressionResult; Const Args: TExprParameterArray);
346- begin
347- Result.resFloat := Sin(DegToRad(ArgToFloat(Args[0]))); // convert radians to degrees
348- end;
349-
350-begin
351- // fpc 3.2+
352- BuiltInIdentifiers.Remove('sin');
353- // or in older versions:
354-{ idx := BuiltinIdentifiers.IndexOfIdentifier('sin');
355- TMyBuiltinManager(BuiltinIdentifiers).Defs.Delete(idx);
356-}
357- BuiltinIdentifiers.AddFunction(bcMath, 'sin', 'F', 'F', @ExprSin);
358-
359- parser := TFPExpressionParser.Create(nil);
360- try
361- parser.BuiltIns := [bcMath];
362- parser.Expression := 'sin(45.0)';
363- WriteLn(parser.Evaluate.ResFloat:0:5);
364- parser.Expression := 'sin(90.0)';
365- WriteLn(parser.Evaluate.ResFloat:0:5);
366- parser.Expression := 'sin(180.0)';
367- WriteLn(parser.Evaluate.ResFloat:0:5);
368- finally
369- parser.Free;
370- end;
371-
372- WriteLn('Press ENTER to close.');
373- ReadLn;
374-end.
315+ Math, fpExprPars;
316+
317+ // these are needed only for fpc before v3.2
318+ {
319+ type
320+ TMyBuiltinManager = class(TExprBuiltinManager);
321+ var
322+ idx: Integer;
323+ }
324+
325+ var
326+ parser: TFPExpressionParser;
327+
328+ Procedure ExprSin(Var Result: TFPExpressionResult; Const Args: TExprParameterArray);
329+ begin
330+ Result.resFloat := Sin(DegToRad(ArgToFloat(Args[0]))); // convert radians to degrees
331+ end;
332+
333+ begin
334+ // fpc 3.2+
335+ BuiltInIdentifiers.Remove('sin');
336+ // or in older versions:
337+ { idx := BuiltinIdentifiers.IndexOfIdentifier('sin');
338+ TMyBuiltinManager(BuiltinIdentifiers).Defs.Delete(idx);
339+ }
340+ BuiltinIdentifiers.AddFunction(bcMath, 'sin', 'F', 'F', @ExprSin);
341+
342+ parser := TFPExpressionParser.Create(nil);
343+ try
344+ parser.BuiltIns := [bcMath];
345+ parser.Expression := 'sin(45.0)';
346+ WriteLn(parser.Evaluate.ResFloat:0:5);
347+ parser.Expression := 'sin(90.0)';
348+ WriteLn(parser.Evaluate.ResFloat:0:5);
349+ parser.Expression := 'sin(180.0)';
350+ WriteLn(parser.Evaluate.ResFloat:0:5);
351+ finally
352+ parser.Free;
353+ end;
354+
355+ WriteLn('Press ENTER to close.');
356+ ReadLn;
357+ end.
375358```
376359
377360---
378-*From the Free Pascal wiki, page "How To Use TFPExpressionParser", as it stood on 2021-10-08 after a change by Wp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
361+*From the Free Pascal wiki, page "How To Use TFPExpressionParser", as the Internet Archive kept it on 2025-04-03, last changed on 2025-03-24. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history