dbf_prscore.pas 30 KB

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