dbf_prscore.pas 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. unit dbf_prscore;
  2. {--------------------------------------------------------------
  3. | TCustomExpressionParser
  4. |
  5. | - contains core expression parser
  6. |---------------------------------------------------------------}
  7. interface
  8. {$I dbf_common.inc}
  9. uses
  10. SysUtils,
  11. Classes,
  12. dbf_common,
  13. dbf_prssupp,
  14. dbf_prsdef;
  15. {$define ENG_NUMBERS}
  16. // ENG_NUMBERS will force the use of english style numbers 8.1 instead of 8,1
  17. // (if the comma is your decimal separator)
  18. // the advantage is that arguments can be separated with a comma which is
  19. // fairly common, otherwise there is ambuigity: what does 'var1,8,4,4,5' mean?
  20. // if you don't define ENG_NUMBERS and DecimalSeparator is a comma then
  21. // the argument separator will be a semicolon ';'
  22. type
  23. TCustomExpressionParser = class(TObject)
  24. private
  25. FHexChar: Char;
  26. FArgSeparator: Char;
  27. FDecimalSeparator: Char;
  28. FOptimize: Boolean;
  29. FConstantsList: TOCollection;
  30. FLastRec: PExpressionRec;
  31. FCurrentRec: PExpressionRec;
  32. FExpResult: PChar;
  33. FExpResultPos: PChar;
  34. FExpResultSize: Integer;
  35. procedure ParseString(AnExpression: string; DestCollection: TExprCollection);
  36. function MakeTree(Expr: TExprCollection; FirstItem, LastItem: Integer): PExpressionRec;
  37. procedure MakeLinkedList(var ExprRec: PExpressionRec; Memory: PPChar;
  38. MemoryPos: PPChar; MemSize: PInteger);
  39. procedure Check(AnExprList: TExprCollection);
  40. procedure CheckArguments(ExprRec: PExpressionRec);
  41. procedure RemoveConstants(var ExprRec: PExpressionRec);
  42. function ResultCanVary(ExprRec: PExpressionRec): Boolean;
  43. protected
  44. FWordsList: TSortedCollection;
  45. function MakeRec: PExpressionRec; virtual;
  46. procedure FillExpressList; virtual; abstract;
  47. procedure HandleUnknownVariable(VarName: string); virtual; abstract;
  48. procedure CompileExpression(AnExpression: string);
  49. procedure EvaluateCurrent;
  50. procedure ReplaceExprWord(OldExprWord, NewExprWord: TExprWord); virtual;
  51. procedure DisposeList(ARec: PExpressionRec);
  52. procedure DisposeTree(ExprRec: PExpressionRec);
  53. function CurrentExpression: string; virtual; abstract;
  54. function GetResultType: TExpressionType; virtual;
  55. property CurrentRec: PExpressionRec read FCurrentRec write FCurrentRec;
  56. property LastRec: PExpressionRec read FLastRec write FLastRec;
  57. property ExpResult: PChar read FExpResult;
  58. property ExpResultPos: PChar read FExpResultPos write FExpResultPos;
  59. public
  60. constructor Create;
  61. destructor Destroy; override;
  62. procedure AddReplaceExprWord(AExprWord: TExprWord);
  63. procedure DefineFloatVariable(AVarName: string; AValue: PDouble);
  64. procedure DefineIntegerVariable(AVarName: string; AValue: PInteger);
  65. // procedure DefineSmallIntVariable(AVarName: string; AValue: PSmallInt);
  66. {$ifdef SUPPORT_INT64}
  67. procedure DefineLargeIntVariable(AVarName: string; AValue: PLargeInt);
  68. {$endif}
  69. procedure DefineDateTimeVariable(AVarName: string; AValue: PDateTimeRec);
  70. procedure DefineBooleanVariable(AVarName: string; AValue: PBoolean);
  71. procedure DefineStringVariable(AVarName: string; AValue: PPChar);
  72. procedure DefineStringVariableFixedLen(AVarName: string; AValue: PPChar; ALength: Integer);
  73. procedure DefineFunction(AFunctName, AShortName, ADescription, ATypeSpec: string;
  74. AMinFunctionArg: Integer; AResultType: TExpressionType; AFuncAddress: TExprFunc);
  75. procedure ReplaceFunction(OldName: string; AFunction: TObject);
  76. procedure Evaluate(AnExpression: string);
  77. function AddExpression(AnExpression: string): Integer;
  78. procedure ClearExpressions; virtual;
  79. // procedure GetGeneratedVars(AList: TList);
  80. procedure GetFunctionNames(AList: TStrings);
  81. function GetFunctionDescription(AFunction: string): string;
  82. property HexChar: Char read FHexChar write FHexChar;
  83. property ArgSeparator: Char read FArgSeparator write FArgSeparator;
  84. property Optimize: Boolean read FOptimize write FOptimize;
  85. property ResultType: TExpressionType read GetResultType;
  86. //if optimize is selected, constant expressions are tried to remove
  87. //such as: 4*4*x is evaluated as 16*x and exp(1)-4*x is repaced by 2.17 -4*x
  88. end;
  89. implementation
  90. { TCustomExpressionParser }
  91. constructor TCustomExpressionParser.Create;
  92. begin
  93. inherited;
  94. FHexChar := '$';
  95. {$IFDEF ENG_NUMBERS}
  96. FDecimalSeparator := '.';
  97. FArgSeparator := ',';
  98. {$ELSE}
  99. FDecimalSeparator := DecimalSeparator;
  100. if DecimalSeparator = ',' then
  101. FArgSeparator := ';'
  102. else
  103. FArgSeparator := ',';
  104. {$ENDIF}
  105. FConstantsList := TOCollection.Create;
  106. FWordsList := TExpressList.Create;
  107. GetMem(FExpResult, ArgAllocSize);
  108. FExpResultPos := FExpResult;
  109. FExpResultSize := ArgAllocSize;
  110. FOptimize := true;
  111. FillExpressList;
  112. end;
  113. destructor TCustomExpressionParser.Destroy;
  114. begin
  115. ClearExpressions;
  116. FreeMem(FExpResult);
  117. FConstantsList.Free;
  118. FWordsList.Free;
  119. inherited;
  120. end;
  121. procedure TCustomExpressionParser.CompileExpression(AnExpression: string);
  122. var
  123. ExpColl: TExprCollection;
  124. ExprTree: PExpressionRec;
  125. begin
  126. if Length(AnExpression) > 0 then
  127. begin
  128. ExprTree := nil;
  129. ExpColl := TExprCollection.Create;
  130. try
  131. // FCurrentExpression := anExpression;
  132. ParseString(AnExpression, ExpColl);
  133. Check(ExpColl);
  134. ExprTree := MakeTree(ExpColl, 0, ExpColl.Count - 1);
  135. FCurrentRec := nil;
  136. CheckArguments(ExprTree);
  137. if Optimize then
  138. RemoveConstants(ExprTree);
  139. // all constant expressions are evaluated and replaced by variables
  140. FCurrentRec := nil;
  141. FExpResultPos := FExpResult;
  142. MakeLinkedList(ExprTree, @FExpResult, @FExpResultPos, @FExpResultSize);
  143. except
  144. on E: Exception do
  145. begin
  146. DisposeTree(ExprTree);
  147. ExpColl.Free;
  148. raise;
  149. end;
  150. end;
  151. ExpColl.Free;
  152. end;
  153. end;
  154. procedure TCustomExpressionParser.CheckArguments(ExprRec: PExpressionRec);
  155. var
  156. TempExprWord: TExprWord;
  157. I, error: Integer;
  158. foundAltFunc: Boolean;
  159. begin
  160. with ExprRec^ do
  161. begin
  162. repeat
  163. I := 0;
  164. error := 0;
  165. foundAltFunc := false;
  166. while (I < ExprWord.MaxFunctionArg) and (ArgList[I] <> nil) and (error = 0) do
  167. begin
  168. // test subarguments first
  169. CheckArguments(ArgList[I]);
  170. // test if correct type
  171. if (ArgList[I]^.ExprWord.ResultType <> ExprCharToExprType(ExprWord.TypeSpec[I+1])) then
  172. error := 2;
  173. // goto next argument
  174. Inc(I);
  175. end;
  176. // test if enough parameters passed; I = num args user passed
  177. if (error = 0) and (I < ExprWord.MinFunctionArg) then
  178. error := 1;
  179. // test if too many parameters passed
  180. if (error = 0) and (I > ExprWord.MaxFunctionArg) then
  181. error := 3;
  182. // error occurred?
  183. if error <> 0 then
  184. begin
  185. // see if we can find another function
  186. I := FWordsList.IndexOf(ExprWord);
  187. // check if not last function
  188. if I < FWordsList.Count - 1 then
  189. begin
  190. TempExprWord := TExprWord(FWordsList.Items[I+1]);
  191. if FWordsList.Compare(FWordsList.KeyOf(ExprWord), FWordsList.KeyOf(TempExprWord)) = 0 then
  192. begin
  193. ExprWord := TempExprWord;
  194. Oper := ExprWord.ExprFunc;
  195. foundAltFunc := true;
  196. end;
  197. end;
  198. end;
  199. until (error = 0) or not foundAltFunc;
  200. // fatal error?
  201. case error of
  202. 1: raise EParserException.Create('Function or operand has too few arguments');
  203. 2: raise EParserException.Create('Argument type mismatch');
  204. 3: raise EParserException.Create('Function or operand has too many arguments');
  205. end;
  206. end;
  207. end;
  208. function TCustomExpressionParser.ResultCanVary(ExprRec: PExpressionRec):
  209. Boolean;
  210. var
  211. I: Integer;
  212. begin
  213. with ExprRec^ do
  214. begin
  215. Result := ExprWord.CanVary;
  216. if not Result then
  217. for I := 0 to ExprWord.MaxFunctionArg - 1 do
  218. if ResultCanVary(ArgList[I]) then
  219. begin
  220. Result := true;
  221. Exit;
  222. end
  223. end;
  224. end;
  225. procedure TCustomExpressionParser.RemoveConstants(var ExprRec: PExpressionRec);
  226. var
  227. I: Integer;
  228. begin
  229. if not ResultCanVary(ExprRec) then
  230. begin
  231. if not ExprRec^.ExprWord.IsVariable then
  232. begin
  233. // reset current record so that make list generates new
  234. FCurrentRec := nil;
  235. FExpResultPos := FExpResult;
  236. MakeLinkedList(ExprRec, @FExpResult, @FExpResultPos, @FExpResultSize);
  237. try
  238. // compute result
  239. EvaluateCurrent;
  240. // make new record to store constant in
  241. ExprRec := MakeRec;
  242. // check result type
  243. with ExprRec^ do
  244. begin
  245. case ResultType of
  246. etBoolean: ExprWord := TBooleanConstant.Create(EmptyStr, PBoolean(FExpResult)^);
  247. etFloat: ExprWord := TFloatConstant.CreateAsDouble(EmptyStr, PDouble(FExpResult)^);
  248. etString: ExprWord := TStringConstant.Create(FExpResult);
  249. end;
  250. // fill in structure
  251. Oper := ExprWord.ExprFunc;
  252. Args[0] := ExprWord.AsPointer;
  253. FConstantsList.Add(ExprWord);
  254. end;
  255. finally
  256. DisposeList(FCurrentRec);
  257. FCurrentRec := nil;
  258. end;
  259. end;
  260. end else
  261. with ExprRec^ do
  262. begin
  263. for I := 0 to ExprWord.MaxFunctionArg - 1 do
  264. if ArgList[I] <> nil then
  265. RemoveConstants(ArgList[I]);
  266. end;
  267. end;
  268. procedure TCustomExpressionParser.DisposeTree(ExprRec: PExpressionRec);
  269. var
  270. I: Integer;
  271. begin
  272. if ExprRec <> nil then
  273. begin
  274. with ExprRec^ do
  275. begin
  276. if ExprWord <> nil then
  277. for I := 0 to ExprWord.MaxFunctionArg - 1 do
  278. DisposeTree(ArgList[I]);
  279. if Res <> nil then
  280. Res.Free;
  281. end;
  282. Dispose(ExprRec);
  283. end;
  284. end;
  285. procedure TCustomExpressionParser.DisposeList(ARec: PExpressionRec);
  286. var
  287. TheNext: PExpressionRec;
  288. I: Integer;
  289. begin
  290. if ARec <> nil then
  291. repeat
  292. TheNext := ARec^.Next;
  293. if ARec^.Res <> nil then
  294. ARec^.Res.Free;
  295. I := 0;
  296. while ARec^.ArgList[I] <> nil do
  297. begin
  298. FreeMem(ARec^.Args[I]);
  299. Inc(I);
  300. end;
  301. Dispose(ARec);
  302. ARec := TheNext;
  303. until ARec = nil;
  304. end;
  305. procedure TCustomExpressionParser.MakeLinkedList(var ExprRec: PExpressionRec;
  306. Memory: PPChar; MemoryPos: PPChar; MemSize: PInteger);
  307. var
  308. I: Integer;
  309. begin
  310. // test function type
  311. if @ExprRec^.ExprWord.ExprFunc = nil then
  312. begin
  313. // special 'no function' function
  314. // indicates no function is present -> we can concatenate all instances
  315. // we don't create new arguments...these 'fall' through
  316. // use destination as we got it
  317. I := 0;
  318. while ExprRec^.ArgList[I] <> nil do
  319. begin
  320. // convert arguments to list
  321. MakeLinkedList(ExprRec^.ArgList[I], Memory, MemoryPos, MemSize);
  322. // goto next argument
  323. Inc(I);
  324. end;
  325. // don't need this record anymore
  326. Dispose(ExprRec);
  327. ExprRec := nil;
  328. end else begin
  329. // inc memory pointer so we know if we are first
  330. ExprRec^.ResetDest := MemoryPos^ = Memory^;
  331. Inc(MemoryPos^);
  332. // convert arguments to list
  333. I := 0;
  334. while ExprRec^.ArgList[I] <> nil do
  335. begin
  336. // save variable type for easy access
  337. ExprRec^.ArgsType[I] := ExprRec^.ArgList[I]^.ExprWord.ResultType;
  338. // check if we need to copy argument, variables in general do not
  339. // need copying, except for fixed len strings which are not
  340. // null-terminated
  341. // if ExprRec^.ArgList[I].ExprWord.NeedsCopy then
  342. // begin
  343. // get memory for argument
  344. GetMem(ExprRec^.Args[I], ArgAllocSize);
  345. ExprRec^.ArgsPos[I] := ExprRec^.Args[I];
  346. ExprRec^.ArgsSize[I] := ArgAllocSize;
  347. MakeLinkedList(ExprRec^.ArgList[I], @ExprRec^.Args[I], @ExprRec^.ArgsPos[I],
  348. @ExprRec^.ArgsSize[I]);
  349. // end else begin
  350. // copy reference
  351. // ExprRec^.Args[I] := ExprRec^.ArgList[I].Args[0];
  352. // ExprRec^.ArgsPos[I] := ExprRec^.Args[I];
  353. // ExprRec^.ArgsSize[I] := 0;
  354. // FreeMem(ExprRec^.ArgList[I]);
  355. // ExprRec^.ArgList[I] := nil;
  356. // end;
  357. // goto next argument
  358. Inc(I);
  359. end;
  360. // link result to target argument
  361. ExprRec^.Res := TDynamicType.Create(Memory, MemoryPos, MemSize);
  362. // link to next operation
  363. if FCurrentRec = nil then
  364. begin
  365. FCurrentRec := ExprRec;
  366. FLastRec := ExprRec;
  367. end else begin
  368. FLastRec^.Next := ExprRec;
  369. FLastRec := ExprRec;
  370. end;
  371. end;
  372. end;
  373. function TCustomExpressionParser.MakeTree(Expr: TExprCollection;
  374. FirstItem, LastItem: Integer): PExpressionRec;
  375. {
  376. - This is the most complex routine, it breaks down the expression and makes
  377. a linked tree which is used for fast function evaluations
  378. - it is implemented recursively
  379. }
  380. var
  381. I, IArg, IStart, IEnd, lPrec, brCount: Integer;
  382. ExprWord: TExprWord;
  383. begin
  384. // remove redundant brackets
  385. brCount := 0;
  386. while (FirstItem+brCount < LastItem) and (TExprWord(
  387. Expr.Items[FirstItem+brCount]).ResultType = etLeftBracket) do
  388. Inc(brCount);
  389. I := LastItem;
  390. while (I > FirstItem) and (TExprWord(
  391. Expr.Items[I]).ResultType = etRightBracket) do
  392. Dec(I);
  393. // test max of start and ending brackets
  394. if brCount > (LastItem-I) then
  395. brCount := LastItem-I;
  396. // count number of bracket pairs completely open from start to end
  397. // IArg is min.brCount
  398. I := FirstItem + brCount;
  399. IArg := brCount;
  400. while (I <= LastItem - brCount) and (brCount > 0) do
  401. begin
  402. case TExprWord(Expr.Items[I]).ResultType of
  403. etLeftBracket: Inc(brCount);
  404. etRightBracket:
  405. begin
  406. Dec(brCount);
  407. if brCount < IArg then
  408. IArg := brCount;
  409. end;
  410. end;
  411. Inc(I);
  412. end;
  413. // useful pair bracket count, is in minimum, is IArg
  414. brCount := IArg;
  415. // check if subexpression closed within (bracket level will be zero)
  416. if brCount > 0 then
  417. begin
  418. Inc(FirstItem, brCount);
  419. Dec(LastItem, brCount);
  420. end;
  421. // check for empty range
  422. if LastItem < FirstItem then
  423. begin
  424. Result := nil;
  425. exit;
  426. end;
  427. // get new record
  428. Result := MakeRec;
  429. // simple constant, variable or function?
  430. if LastItem = FirstItem then
  431. begin
  432. Result^.ExprWord := TExprWord(Expr.Items[FirstItem]);
  433. Result^.Oper := Result^.ExprWord.ExprFunc;
  434. if Result^.ExprWord.IsVariable then
  435. begin
  436. // copy pointer to variable
  437. Result^.Args[0] := Result^.ExprWord.AsPointer;
  438. // is this a fixed length string variable?
  439. if Result^.ExprWord.FixedLen >= 0 then
  440. begin
  441. // store length as second parameter
  442. Result^.Args[1] := PChar(Result^.ExprWord.LenAsPointer);
  443. end;
  444. end;
  445. exit;
  446. end;
  447. // no...more complex, find operator with lowest precedence
  448. brCount := 0;
  449. IArg := 0;
  450. IEnd := FirstItem-1;
  451. lPrec := -1;
  452. for I := FirstItem to LastItem do
  453. begin
  454. ExprWord := TExprWord(Expr.Items[I]);
  455. if (brCount = 0) and ExprWord.IsOperator and (TFunction(ExprWord).OperPrec > lPrec) then
  456. begin
  457. IEnd := I;
  458. lPrec := TFunction(ExprWord).OperPrec;
  459. end;
  460. case ExprWord.ResultType of
  461. etLeftBracket: Inc(brCount);
  462. etRightBracket: Dec(brCount);
  463. end;
  464. end;
  465. // operator found ?
  466. if IEnd >= FirstItem then
  467. begin
  468. // save operator
  469. Result^.ExprWord := TExprWord(Expr.Items[IEnd]);
  470. Result^.Oper := Result^.ExprWord.ExprFunc;
  471. // recurse into left part if present
  472. if IEnd > FirstItem then
  473. begin
  474. Result^.ArgList[IArg] := MakeTree(Expr, FirstItem, IEnd-1);
  475. Inc(IArg);
  476. end;
  477. // recurse into right part if present
  478. if IEnd < LastItem then
  479. Result^.ArgList[IArg] := MakeTree(Expr, IEnd+1, LastItem);
  480. end else
  481. if TExprWord(Expr.Items[FirstItem]).IsFunction then
  482. begin
  483. // save function
  484. Result^.ExprWord := TExprWord(Expr.Items[FirstItem]);
  485. Result^.Oper := Result^.ExprWord.ExprFunc;
  486. // parse function arguments
  487. IEnd := FirstItem + 1;
  488. IStart := IEnd;
  489. brCount := 0;
  490. if TExprWord(Expr.Items[IEnd]).ResultType = etLeftBracket then
  491. begin
  492. // opening bracket found, first argument expression starts at next index
  493. Inc(brCount);
  494. Inc(IStart);
  495. while (IEnd < LastItem) and (brCount <> 0) do
  496. begin
  497. Inc(IEnd);
  498. case TExprWord(Expr.Items[IEnd]).ResultType of
  499. etLeftBracket: Inc(brCount);
  500. etComma:
  501. if brCount = 1 then
  502. begin
  503. // argument separation found, build tree of argument expression
  504. Result^.ArgList[IArg] := MakeTree(Expr, IStart, IEnd-1);
  505. Inc(IArg);
  506. IStart := IEnd + 1;
  507. end;
  508. etRightBracket: Dec(brCount);
  509. end;
  510. end;
  511. // parse last argument
  512. Result^.ArgList[IArg] := MakeTree(Expr, IStart, IEnd-1);
  513. end;
  514. end else
  515. raise EParserException.Create('Operator/function missing');
  516. end;
  517. procedure TCustomExpressionParser.ParseString(AnExpression: string; DestCollection: TExprCollection);
  518. var
  519. isConstant: Boolean;
  520. I, I1, I2, Len, DecSep: Integer;
  521. W, S: string;
  522. TempWord: TExprWord;
  523. procedure ReadConstant(AnExpr: string; isHex: Boolean);
  524. begin
  525. isConstant := true;
  526. while (I2 <= Len) and ((AnExpr[I2] in ['0'..'9']) or
  527. (isHex and (AnExpr[I2] in ['a'..'f', 'A'..'F']))) do
  528. Inc(I2);
  529. if I2 <= Len then
  530. begin
  531. if AnExpr[I2] = FDecimalSeparator then
  532. begin
  533. Inc(I2);
  534. while (I2 <= Len) and (AnExpr[I2] in ['0'..'9']) do
  535. Inc(I2);
  536. end;
  537. if (I2 <= Len) and (AnExpr[I2] = 'e') then
  538. begin
  539. Inc(I2);
  540. if (I2 <= Len) and (AnExpr[I2] in ['+', '-']) then
  541. Inc(I2);
  542. while (I2 <= Len) and (AnExpr[I2] in ['0'..'9']) do
  543. Inc(I2);
  544. end;
  545. end;
  546. end;
  547. procedure ReadWord(AnExpr: string);
  548. var
  549. OldI2: Integer;
  550. constChar: Char;
  551. begin
  552. isConstant := false;
  553. I1 := I2;
  554. while (I1 < Len) and (AnExpr[I1] = ' ') do
  555. Inc(I1);
  556. I2 := I1;
  557. if I1 <= Len then
  558. begin
  559. if AnExpr[I2] = HexChar then
  560. begin
  561. Inc(I2);
  562. OldI2 := I2;
  563. ReadConstant(AnExpr, true);
  564. if I2 = OldI2 then
  565. begin
  566. isConstant := false;
  567. while (I2 <= Len) and (AnExpr[I2] in ['a'..'z', 'A'..'Z', '_', '0'..'9']) do
  568. Inc(I2);
  569. end;
  570. end
  571. else if AnExpr[I2] = FDecimalSeparator then
  572. ReadConstant(AnExpr, false)
  573. else
  574. case AnExpr[I2] of
  575. '''', '"':
  576. begin
  577. isConstant := true;
  578. constChar := AnExpr[I2];
  579. Inc(I2);
  580. while (I2 <= Len) and (AnExpr[I2] <> constChar) do
  581. Inc(I2);
  582. if I2 <= Len then
  583. Inc(I2);
  584. end;
  585. 'a'..'z', 'A'..'Z', '_':
  586. begin
  587. while (I2 <= Len) and (AnExpr[I2] in ['a'..'z', 'A'..'Z', '_', '0'..'9']) do
  588. Inc(I2);
  589. end;
  590. '>', '<':
  591. begin
  592. if (I2 <= Len) then
  593. Inc(I2);
  594. if AnExpr[I2] in ['=', '<', '>'] then
  595. Inc(I2);
  596. end;
  597. '=':
  598. begin
  599. if (I2 <= Len) then
  600. Inc(I2);
  601. if AnExpr[I2] in ['<', '>', '='] then
  602. Inc(I2);
  603. end;
  604. '&':
  605. begin
  606. if (I2 <= Len) then
  607. Inc(I2);
  608. if AnExpr[I2] in ['&'] then
  609. Inc(I2);
  610. end;
  611. '|':
  612. begin
  613. if (I2 <= Len) then
  614. Inc(I2);
  615. if AnExpr[I2] in ['|'] then
  616. Inc(I2);
  617. end;
  618. ':':
  619. begin
  620. if (I2 <= Len) then
  621. Inc(I2);
  622. if AnExpr[I2] = '=' then
  623. Inc(I2);
  624. end;
  625. '!':
  626. begin
  627. if (I2 <= Len) then
  628. Inc(I2);
  629. if AnExpr[I2] = '=' then //support for !=
  630. Inc(I2);
  631. end;
  632. '+':
  633. begin
  634. Inc(I2);
  635. if (AnExpr[I2] = '+') and FWordsList.Search(PChar('++'), I) then
  636. Inc(I2);
  637. end;
  638. '-':
  639. begin
  640. Inc(I2);
  641. if (AnExpr[I2] = '-') and FWordsList.Search(PChar('--'), I) then
  642. Inc(I2);
  643. end;
  644. '^', '/', '\', '*', '(', ')', '%', '~', '$':
  645. Inc(I2);
  646. '0'..'9':
  647. ReadConstant(AnExpr, false);
  648. else
  649. begin
  650. Inc(I2);
  651. end;
  652. end;
  653. end;
  654. end;
  655. begin
  656. I2 := 1;
  657. S := Trim(AnExpression);
  658. Len := Length(S);
  659. repeat
  660. ReadWord(S);
  661. W := Trim(Copy(S, I1, I2 - I1));
  662. if isConstant then
  663. begin
  664. if W[1] = HexChar then
  665. begin
  666. // convert hexadecimal to decimal
  667. W[1] := '$';
  668. W := IntToStr(StrToInt(W));
  669. end;
  670. if (W[1] = '''') or (W[1] = '"') then
  671. TempWord := TStringConstant.Create(W)
  672. else begin
  673. DecSep := Pos(FDecimalSeparator, W);
  674. if (DecSep > 0) then
  675. begin
  676. {$IFDEF ENG_NUMBERS}
  677. // we'll have to convert FDecimalSeparator into DecimalSeparator
  678. // otherwise the OS will not understand what we mean
  679. W[DecSep] := DecimalSeparator;
  680. {$ENDIF}
  681. TempWord := TFloatConstant.Create(W, W)
  682. end else begin
  683. TempWord := TIntegerConstant.Create(StrToInt(W));
  684. end;
  685. end;
  686. DestCollection.Add(TempWord);
  687. FConstantsList.Add(TempWord);
  688. end
  689. else if Length(W) > 0 then
  690. if FWordsList.Search(PChar(W), I) then
  691. begin
  692. DestCollection.Add(FWordsList.Items[I])
  693. end else begin
  694. // unknown variable -> fire event
  695. HandleUnknownVariable(W);
  696. // try to search again
  697. if FWordsList.Search(PChar(W), I) then
  698. begin
  699. DestCollection.Add(FWordsList.Items[I])
  700. end else begin
  701. raise EParserException.Create('Unknown variable '''+W+''' found.');
  702. end;
  703. end;
  704. until I2 > Len;
  705. end;
  706. procedure TCustomExpressionParser.Check(AnExprList: TExprCollection);
  707. var
  708. I, J, K, L: Integer;
  709. begin
  710. AnExprList.Check;
  711. with AnExprList do
  712. begin
  713. I := 0;
  714. while I < Count do
  715. begin
  716. {----CHECK ON DOUBLE MINUS OR DOUBLE PLUS----}
  717. if ((TExprWord(Items[I]).Name = '-') or
  718. (TExprWord(Items[I]).Name = '+'))
  719. and ((I = 0) or
  720. (TExprWord(Items[I - 1]).ResultType = etComma) or
  721. (TExprWord(Items[I - 1]).ResultType = etLeftBracket) or
  722. (TExprWord(Items[I - 1]).IsOperator and (TExprWord(Items[I - 1]).MaxFunctionArg
  723. = 2))) then
  724. begin
  725. {replace e.g. ----1 with +1}
  726. if TExprWord(Items[I]).Name = '-' then
  727. K := -1
  728. else
  729. K := 1;
  730. L := 1;
  731. while (I + L < Count) and ((TExprWord(Items[I + L]).Name = '-')
  732. or (TExprWord(Items[I + L]).Name = '+')) and ((I + L = 0) or
  733. (TExprWord(Items[I + L - 1]).ResultType = etComma) or
  734. (TExprWord(Items[I + L - 1]).ResultType = etLeftBracket) or
  735. (TExprWord(Items[I + L - 1]).IsOperator and (TExprWord(Items[I + L -
  736. 1]).MaxFunctionArg = 2))) do
  737. begin
  738. if TExprWord(Items[I + L]).Name = '-' then
  739. K := -1 * K;
  740. Inc(L);
  741. end;
  742. if L > 0 then
  743. begin
  744. Dec(L);
  745. for J := I + 1 to Count - 1 - L do
  746. Items[J] := Items[J + L];
  747. Count := Count - L;
  748. end;
  749. if K = -1 then
  750. begin
  751. if FWordsList.Search(pchar('-@'), J) then
  752. Items[I] := FWordsList.Items[J];
  753. end
  754. else if FWordsList.Search(pchar('+@'), J) then
  755. Items[I] := FWordsList.Items[J];
  756. end;
  757. {----CHECK ON DOUBLE NOT----}
  758. if (TExprWord(Items[I]).Name = 'not')
  759. and ((I = 0) or
  760. (TExprWord(Items[I - 1]).ResultType = etLeftBracket) or
  761. TExprWord(Items[I - 1]).IsOperator) then
  762. begin
  763. {replace e.g. not not 1 with 1}
  764. K := -1;
  765. L := 1;
  766. while (I + L < Count) and (TExprWord(Items[I + L]).Name = 'not') and ((I
  767. + L = 0) or
  768. (TExprWord(Items[I + L - 1]).ResultType = etLeftBracket) or
  769. TExprWord(Items[I + L - 1]).IsOperator) do
  770. begin
  771. K := -K;
  772. Inc(L);
  773. end;
  774. if L > 0 then
  775. begin
  776. if K = 1 then
  777. begin //remove all
  778. for J := I to Count - 1 - L do
  779. Items[J] := Items[J + L];
  780. Count := Count - L;
  781. end
  782. else
  783. begin //keep one
  784. Dec(L);
  785. for J := I + 1 to Count - 1 - L do
  786. Items[J] := Items[J + L];
  787. Count := Count - L;
  788. end
  789. end;
  790. end;
  791. {-----MISC CHECKS-----}
  792. if (TExprWord(Items[I]).IsVariable) and ((I < Count - 1) and
  793. (TExprWord(Items[I + 1]).IsVariable)) then
  794. raise EParserException.Create('Missing operator between '''+TExprWord(Items[I]).Name+''' and '''+TExprWord(Items[I]).Name+'''');
  795. if (TExprWord(Items[I]).ResultType = etLeftBracket) and ((I >= Count - 1) or
  796. (TExprWord(Items[I + 1]).ResultType = etRightBracket)) then
  797. raise EParserException.Create('Empty brackets ()');
  798. if (TExprWord(Items[I]).ResultType = etRightBracket) and ((I < Count - 1) and
  799. (TExprWord(Items[I + 1]).ResultType = etLeftBracket)) then
  800. raise EParserException.Create('Missing operator between )(');
  801. if (TExprWord(Items[I]).ResultType = etRightBracket) and ((I < Count - 1) and
  802. (TExprWord(Items[I + 1]).IsVariable)) then
  803. raise EParserException.Create('Missing operator between ) and constant/variable');
  804. if (TExprWord(Items[I]).ResultType = etLeftBracket) and ((I > 0) and
  805. (TExprWord(Items[I - 1]).IsVariable)) then
  806. raise EParserException.Create('Missing operator between constant/variable and (');
  807. {-----CHECK ON INTPOWER------}
  808. if (TExprWord(Items[I]).Name = '^') and ((I < Count - 1) and
  809. (TExprWord(Items[I + 1]).ClassType = TIntegerConstant)) then
  810. if FWordsList.Search(PChar('^@'), J) then
  811. Items[I] := FWordsList.Items[J]; //use the faster intPower if possible
  812. Inc(I);
  813. end;
  814. end;
  815. end;
  816. procedure TCustomExpressionParser.EvaluateCurrent;
  817. var
  818. TempRec: PExpressionRec;
  819. begin
  820. if FCurrentRec <> nil then
  821. begin
  822. // get current record
  823. TempRec := FCurrentRec;
  824. // execute list
  825. repeat
  826. with TempRec^ do
  827. begin
  828. // do we need to reset pointer?
  829. if ResetDest then
  830. Res.MemoryPos^ := Res.Memory^;
  831. Oper(TempRec);
  832. // goto next
  833. TempRec := Next;
  834. end;
  835. until TempRec = nil;
  836. end;
  837. end;
  838. procedure TCustomExpressionParser.DefineFunction(AFunctName, AShortName, ADescription, ATypeSpec: string;
  839. AMinFunctionArg: Integer; AResultType: TExpressionType; AFuncAddress: TExprFunc);
  840. begin
  841. AddReplaceExprWord(TFunction.Create(AFunctName, AShortName, ATypeSpec, AMinFunctionArg, AResultType, AFuncAddress, ADescription));
  842. end;
  843. procedure TCustomExpressionParser.DefineIntegerVariable(AVarName: string; AValue: PInteger);
  844. begin
  845. AddReplaceExprWord(TIntegerVariable.Create(AVarName, AValue));
  846. end;
  847. {
  848. procedure TCustomExpressionParser.DefineSmallIntVariable(AVarName: string; AValue: PSmallInt);
  849. begin
  850. AddReplaceExprWord(TSmallIntVariable.Create(AVarName, AValue));
  851. end;
  852. }
  853. {$ifdef SUPPORT_INT64}
  854. procedure TCustomExpressionParser.DefineLargeIntVariable(AVarName: string; AValue: PLargeInt);
  855. begin
  856. AddReplaceExprWord(TLargeIntVariable.Create(AVarName, AValue));
  857. end;
  858. {$endif}
  859. procedure TCustomExpressionParser.DefineDateTimeVariable(AVarName: string; AValue: PDateTimeRec);
  860. begin
  861. AddReplaceExprWord(TDateTimeVariable.Create(AVarName, AValue));
  862. end;
  863. procedure TCustomExpressionParser.DefineBooleanVariable(AVarName: string; AValue: PBoolean);
  864. begin
  865. AddReplaceExprWord(TBooleanVariable.Create(AVarName, AValue));
  866. end;
  867. procedure TCustomExpressionParser.DefineFloatVariable(AVarName: string; AValue: PDouble);
  868. begin
  869. AddReplaceExprWord(TFloatVariable.Create(AVarName, AValue));
  870. end;
  871. procedure TCustomExpressionParser.DefineStringVariable(AVarName: string; AValue: PPChar);
  872. begin
  873. DefineStringVariableFixedLen(AVarName, AValue, -1);
  874. end;
  875. procedure TCustomExpressionParser.DefineStringVariableFixedLen(AVarName: string; AValue: PPChar; ALength: Integer);
  876. begin
  877. AddReplaceExprWord(TStringVariable.Create(AVarName, AValue, ALength));
  878. end;
  879. {
  880. procedure TCustomExpressionParser.GetGeneratedVars(AList: TList);
  881. var
  882. I: Integer;
  883. begin
  884. AList.Clear;
  885. with FWordsList do
  886. for I := 0 to Count - 1 do
  887. begin
  888. if TObject(Items[I]).ClassType = TGeneratedVariable then
  889. AList.Add(Items[I]);
  890. end;
  891. end;
  892. }
  893. function TCustomExpressionParser.GetResultType: TExpressionType;
  894. begin
  895. Result := etUnknown;
  896. if FCurrentRec <> nil then
  897. begin
  898. //LAST operand should be boolean -otherwise If(,,) doesn't work
  899. while (FLastRec^.Next <> nil) do
  900. FLastRec := FLastRec^.Next;
  901. if FLastRec^.ExprWord <> nil then
  902. Result := FLastRec^.ExprWord.ResultType;
  903. end;
  904. end;
  905. procedure TCustomExpressionParser.ReplaceExprWord(OldExprWord, NewExprWord: TExprWord);
  906. var
  907. J: Integer;
  908. Rec: PExpressionRec;
  909. p, pnew: pointer;
  910. begin
  911. if OldExprWord.MaxFunctionArg <> NewExprWord.MaxFunctionArg then
  912. raise Exception.Create('Cannot replace variable/function MaxFunctionArg doesn''t match');
  913. p := OldExprWord.AsPointer;
  914. pnew := NewExprWord.AsPointer;
  915. Rec := FCurrentRec;
  916. repeat
  917. if (Rec^.ExprWord = OldExprWord) then
  918. begin
  919. Rec^.ExprWord := NewExprWord;
  920. Rec^.Oper := NewExprWord.ExprFunc;
  921. end;
  922. if p <> nil then
  923. for J := 0 to Rec^.ExprWord.MaxFunctionArg - 1 do
  924. if Rec^.Args[J] = p then
  925. Rec^.Args[J] := pnew;
  926. Rec := Rec^.Next;
  927. until Rec = nil;
  928. end;
  929. function TCustomExpressionParser.MakeRec: PExpressionRec;
  930. var
  931. I: Integer;
  932. begin
  933. New(Result);
  934. Result^.Oper := nil;
  935. Result^.AuxData := nil;
  936. for I := 0 to MaxArg - 1 do
  937. begin
  938. Result^.Args[I] := nil;
  939. Result^.ArgsPos[I] := nil;
  940. Result^.ArgsSize[I] := 0;
  941. Result^.ArgsType[I] := etUnknown;
  942. Result^.ArgList[I] := nil;
  943. end;
  944. Result^.Res := nil;
  945. Result^.Next := nil;
  946. Result^.ExprWord := nil;
  947. Result^.ResetDest := false;
  948. end;
  949. procedure TCustomExpressionParser.Evaluate(AnExpression: string);
  950. begin
  951. if Length(AnExpression) > 0 then
  952. begin
  953. AddExpression(AnExpression);
  954. EvaluateCurrent;
  955. end;
  956. end;
  957. function TCustomExpressionParser.AddExpression(AnExpression: string): Integer;
  958. begin
  959. if Length(AnExpression) > 0 then
  960. begin
  961. Result := 0;
  962. CompileExpression(AnExpression);
  963. end else
  964. Result := -1;
  965. //CurrentIndex := Result;
  966. end;
  967. procedure TCustomExpressionParser.ReplaceFunction(OldName: string; AFunction:
  968. TObject);
  969. var
  970. I: Integer;
  971. begin
  972. // clearing only allowed when expression is not present
  973. if (AFunction = nil) and (FCurrentRec <> nil) then
  974. raise Exception.Create('Cannot undefine function/variable while expression present');
  975. if FWordsList.Search(PChar(OldName), I) then
  976. begin
  977. // if no function specified, then no need to replace!
  978. if AFunction <> nil then
  979. ReplaceExprWord(TExprWord(FWordsList.Items[I]), TExprWord(AFunction));
  980. FWordsList.AtFree(I);
  981. end;
  982. if AFunction <> nil then
  983. FWordsList.Add(AFunction);
  984. end;
  985. procedure TCustomExpressionParser.ClearExpressions;
  986. begin
  987. DisposeList(FCurrentRec);
  988. FCurrentRec := nil;
  989. FLastRec := nil;
  990. end;
  991. procedure TCustomExpressionParser.AddReplaceExprWord(AExprWord: TExprWord);
  992. var
  993. IOldVar: Integer;
  994. begin
  995. if FWordsList.Search(PChar(AExprWord.Name), IOldVar) then
  996. begin
  997. ReplaceExprWord(TExprWord(FWordsList.Items[IOldVar]), AExprWord);
  998. FWordsList.AtFree(IOldVar);
  999. FWordsList.Add(AExprWord);
  1000. end
  1001. else
  1002. FWordsList.Add(AExprWord);
  1003. end;
  1004. function TCustomExpressionParser.GetFunctionDescription(AFunction: string):
  1005. string;
  1006. var
  1007. S: string;
  1008. p, I: Integer;
  1009. begin
  1010. S := AFunction;
  1011. p := Pos('(', S);
  1012. if p > 0 then
  1013. S := Copy(S, 1, p - 1);
  1014. if FWordsList.Search(pchar(S), I) then
  1015. Result := TExprWord(FWordsList.Items[I]).Description
  1016. else
  1017. Result := EmptyStr;
  1018. end;
  1019. procedure TCustomExpressionParser.GetFunctionNames(AList: TStrings);
  1020. var
  1021. I, J: Integer;
  1022. S: string;
  1023. begin
  1024. with FWordsList do
  1025. for I := 0 to Count - 1 do
  1026. with TExprWord(FWordsList.Items[I]) do
  1027. if Length(Description) > 0 then
  1028. begin
  1029. S := Name;
  1030. if MaxFunctionArg > 0 then
  1031. begin
  1032. S := S + '(';
  1033. for J := 0 to MaxFunctionArg - 2 do
  1034. S := S + ArgSeparator;
  1035. S := S + ')';
  1036. end;
  1037. AList.Add(S);
  1038. end;
  1039. end;
  1040. end.