home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 September / Chip_2001-09_cd1.bin / zkuste / delphi / kolekce / d6 / RX275D6.ZIP / Units / PARSING.PAS < prev    next >
Pascal/Delphi Source File  |  1999-10-12  |  14KB  |  492 lines

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1995, 1996 AO ROSNO             }
  6. {         Copyright (c) 1997, 1998 Master-Bank          }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. unit Parsing;
  11.  
  12. interface
  13.  
  14. {$I RX.INC}
  15.  
  16. uses SysUtils, Classes;
  17.  
  18. type
  19.   TParserFunc = (pfArcTan, pfCos, pfSin, pfTan, pfAbs, pfExp, pfLn, pfLog,
  20.     pfSqrt, pfSqr, pfInt, pfFrac, pfTrunc, pfRound, pfArcSin, pfArcCos,
  21.     pfSign, pfNot);
  22.   ERxParserError = class(Exception);
  23. {$IFDEF WIN32}
  24.   TUserFunction = function(Value: Extended): Extended;
  25. {$ELSE}
  26.   TUserFunction = Pointer;
  27. {$ENDIF}
  28.  
  29.   TRxMathParser = class(TObject)
  30.   private
  31.     FCurPos: Cardinal;
  32.     FParseText: string;
  33.     function GetChar: Char;
  34.     procedure NextChar;
  35.     function GetNumber(var AValue: Extended): Boolean;
  36.     function GetConst(var AValue: Extended): Boolean;
  37.     function GetFunction(var AValue: TParserFunc): Boolean;
  38.     function GetUserFunction(var Index: Integer): Boolean;
  39.     function Term: Extended;
  40.     function SubTerm: Extended;
  41.     function Calculate: Extended;
  42.   public
  43.     function Exec(const AFormula: string): Extended;
  44.     class procedure RegisterUserFunction(const Name: string; Proc: TUserFunction);
  45.     class procedure UnregisterUserFunction(const Name: string);
  46.   end;
  47.  
  48. function GetFormulaValue(const Formula: string): Extended;
  49.  
  50. {$IFNDEF WIN32}
  51. function Power(Base, Exponent: Extended): Extended;
  52. {$ENDIF}
  53.  
  54. implementation
  55.  
  56. uses RxTConst;
  57.  
  58. const
  59.   SpecialChars = [#0..' ', '+', '-', '/', '*', ')', '^'];
  60.  
  61.   FuncNames: array[TParserFunc] of PChar =
  62.     ('ARCTAN', 'COS', 'SIN', 'TAN', 'ABS', 'EXP', 'LN', 'LOG',
  63.     'SQRT', 'SQR', 'INT', 'FRAC', 'TRUNC', 'ROUND', 'ARCSIN', 'ARCCOS',
  64.     'SIGN', 'NOT');
  65.  
  66. { Parser errors }
  67.  
  68. procedure InvalidCondition(Str: Word);
  69. begin
  70.   raise ERxParserError.Create(LoadStr(Str));
  71. end;
  72.  
  73. { IntPower and Power functions are copied from Borland's MATH.PAS unit }
  74.  
  75. function IntPower(Base: Extended; Exponent: Integer): Extended;
  76. {$IFDEF WIN32}
  77. asm
  78.         mov     ecx, eax
  79.         cdq
  80.         fld1                      { Result := 1 }
  81.         xor     eax, edx
  82.         sub     eax, edx          { eax := Abs(Exponent) }
  83.         jz      @@3
  84.         fld     Base
  85.         jmp     @@2
  86. @@1:    fmul    ST, ST            { X := Base * Base }
  87. @@2:    shr     eax,1
  88.         jnc     @@1
  89.         fmul    ST(1),ST          { Result := Result * X }
  90.         jnz     @@1
  91.         fstp    st                { pop X from FPU stack }
  92.         cmp     ecx, 0
  93.         jge     @@3
  94.         fld1
  95.         fdivrp                    { Result := 1 / Result }
  96. @@3:
  97.         fwait
  98. end;
  99. {$ELSE}
  100. var
  101.   Y: Longint;
  102. begin
  103.   Y := Abs(Exponent);
  104.   Result := 1.0;
  105.   while Y > 0 do begin
  106.     while not Odd(Y) do begin
  107.       Y := Y shr 1;
  108.       Base := Base * Base;
  109.     end;
  110.     Dec(Y);
  111.     Result := Result * Base;
  112.   end;
  113.   if Exponent < 0 then Result := 1.0 / Result;
  114. end;
  115. {$ENDIF WIN32}
  116.  
  117. function Power(Base, Exponent: Extended): Extended;
  118. begin
  119.   if Exponent = 0.0 then Result := 1.0
  120.   else if (Base = 0.0) and (Exponent > 0.0) then Result := 0.0
  121.   else if (Frac(Exponent) = 0.0) and (Abs(Exponent) <= MaxInt) then
  122.     Result := IntPower(Base, Trunc(Exponent))
  123.   else Result := Exp(Exponent * Ln(Base))
  124. end;
  125.  
  126. { User defined functions }
  127.  
  128. type
  129. {$IFDEF WIN32}
  130.   TFarUserFunction = TUserFunction;
  131. {$ELSE}
  132.   TFarUserFunction = function(Value: Extended): Extended;
  133. {$ENDIF}
  134.  
  135. var
  136.   UserFuncList: TStrings;
  137.  
  138. function GetUserFuncList: TStrings;
  139. begin
  140.   if not Assigned(UserFuncList) then begin
  141.     UserFuncList := TStringList.Create;
  142.     with TStringList(UserFuncList) do begin
  143.       Sorted := True;
  144.       Duplicates := dupIgnore;
  145.     end;
  146.   end;
  147.   Result := UserFuncList;
  148. end;
  149.  
  150. procedure FreeUserFunc; far;
  151. begin
  152.   UserFuncList.Free;
  153.   UserFuncList := nil;
  154. end;
  155.  
  156. { Parsing routines }
  157.  
  158. function GetFormulaValue(const Formula: string): Extended;
  159. begin
  160.   with TRxMathParser.Create do
  161.   try
  162.     Result := Exec(Formula);
  163.   finally
  164.     Free;
  165.   end;
  166. end;
  167.  
  168. { TRxMathParser }
  169.  
  170. function TRxMathParser.GetChar: Char;
  171. begin
  172.   Result := FParseText[FCurPos];
  173. end;
  174.  
  175. procedure TRxMathParser.NextChar;
  176. begin
  177.   Inc(FCurPos);
  178. end;
  179.  
  180. function TRxMathParser.GetNumber(var AValue: Extended): Boolean;
  181. var
  182.   C: Char;
  183.   SavePos: Cardinal;
  184.   Code: Integer;
  185.   IsHex: Boolean;
  186.   TmpStr: string;
  187. begin
  188.   Result := False;
  189.   C := GetChar;
  190.   SavePos := FCurPos;
  191.   TmpStr := '';
  192.   IsHex := False;
  193.   if C = '$' then begin
  194.     TmpStr := C;
  195.     NextChar;
  196.     C := GetChar;
  197.     while C in ['0'..'9', 'A'..'F', 'a'..'f'] do begin
  198.       TmpStr := TmpStr + C;
  199.       NextChar;
  200.       C := GetChar;
  201.     end;
  202.     IsHex := True;
  203.     Result := (Length(TmpStr) > 1) and (Length(TmpStr) <= 9);
  204.   end
  205.   else if C in ['+', '-', '0'..'9', '.', DecimalSeparator] then begin
  206.     if (C in ['.', DecimalSeparator]) then TmpStr := '0' + '.'
  207.     else TmpStr := C;
  208.     NextChar;
  209.     C := GetChar;
  210.     if (Length(TmpStr) = 1) and (TmpStr[1] in ['+', '-']) and
  211.       (C in ['.', DecimalSeparator]) then TmpStr := TmpStr + '0';
  212.     while C in ['0'..'9', '.', 'E', 'e', DecimalSeparator] do begin
  213.       if C = DecimalSeparator then TmpStr := TmpStr + '.'
  214.       else TmpStr := TmpStr + C;
  215.       if (C = 'E') then begin
  216.         if (Length(TmpStr) > 1) and (TmpStr[Length(TmpStr) - 1] = '.') then
  217.           Insert('0', TmpStr, Length(TmpStr));
  218.         NextChar;
  219.         C := GetChar;
  220.         if (C in ['+', '-']) then begin
  221.           TmpStr := TmpStr + C;
  222.           NextChar;
  223.         end;
  224.       end
  225.       else NextChar;
  226.       C := GetChar;
  227.     end;
  228.     if (TmpStr[Length(TmpStr)] = '.') and (Pos('E', TmpStr) = 0) then
  229.       TmpStr := TmpStr + '0';
  230.     Val(TmpStr, AValue, Code);
  231.     Result := (Code = 0);
  232.   end;
  233.   Result := Result and (FParseText[FCurPos] in SpecialChars);
  234.   if Result then begin
  235.     if IsHex then AValue := StrToInt(TmpStr)
  236.     { else AValue := StrToFloat(TmpStr) };
  237.   end
  238.   else begin
  239.     AValue := 0;
  240.     FCurPos := SavePos;
  241.   end;
  242. end;
  243.  
  244. function TRxMathParser.GetConst(var AValue: Extended): Boolean;
  245. begin
  246.   Result := False;
  247.   case FParseText[FCurPos] of
  248.     'E':
  249.       if FParseText[FCurPos + 1] in SpecialChars then
  250.       begin
  251.         AValue := Exp(1);
  252.         Inc(FCurPos);
  253.         Result := True;
  254.       end;
  255.     'P':
  256.       if (FParseText[FCurPos + 1] = 'I') and
  257.         (FParseText[FCurPos + 2] in SpecialChars) then
  258.       begin
  259.         AValue := Pi;
  260.         Inc(FCurPos, 2);
  261.         Result := True;
  262.       end;
  263.   end
  264. end;
  265.  
  266. function TRxMathParser.GetUserFunction(var Index: Integer): Boolean;
  267. var
  268.   TmpStr: string;
  269.   I: Integer;
  270. begin
  271.   Result := False;
  272.   if (FParseText[FCurPos] in ['A'..'Z', 'a'..'z', '_']) and
  273.     Assigned(UserFuncList) then
  274.   begin
  275.     with UserFuncList do
  276.       for I := 0 to Count - 1 do begin
  277.         TmpStr := Copy(FParseText, FCurPos, Length(Strings[I]));
  278.         if (CompareText(TmpStr, Strings[I]) = 0) and
  279.           (Objects[I] <> nil) then
  280.         begin
  281.           if FParseText[FCurPos + Cardinal(Length(TmpStr))] = '(' then
  282.           begin
  283.             Result := True;
  284.             Inc(FCurPos, Length(TmpStr));
  285.             Index := I;
  286.             Exit;
  287.           end;
  288.         end;
  289.       end;
  290.   end;
  291.   Index := -1;
  292. end;
  293.  
  294. function TRxMathParser.GetFunction(var AValue: TParserFunc): Boolean;
  295. var
  296.   I: TParserFunc;
  297.   TmpStr: string;
  298. begin
  299.   Result := False;
  300.   AValue := Low(TParserFunc);
  301.   if FParseText[FCurPos] in ['A'..'Z', 'a'..'z', '_'] then begin
  302.     for I := Low(TParserFunc) to High(TParserFunc) do begin
  303.       TmpStr := Copy(FParseText, FCurPos, StrLen(FuncNames[I]));
  304.       if CompareText(TmpStr, StrPas(FuncNames[I])) = 0 then begin
  305.         AValue := I;
  306.         if FParseText[FCurPos + Cardinal(Length(TmpStr))] = '(' then begin
  307.           Result := True;
  308.           Inc(FCurPos, Length(TmpStr));
  309.           Break;
  310.         end;
  311.       end;
  312.     end;
  313.   end;
  314. end;
  315.  
  316. function TRxMathParser.Term: Extended;
  317. var
  318.   Value: Extended;
  319.   NoFunc: TParserFunc;
  320.   UserFunc: Integer;
  321.   Func: Pointer;
  322. begin
  323.   if FParseText[FCurPos] = '(' then begin
  324.     Inc(FCurPos);
  325.     Value := Calculate;
  326.     if FParseText[FCurPos] <> ')' then InvalidCondition(SParseNotCramp);
  327.     Inc(FCurPos);
  328.   end
  329.   else begin
  330.     if not GetNumber(Value) then
  331.       if not GetConst(Value) then
  332.         if GetUserFunction(UserFunc) then begin
  333.           Inc(FCurPos);
  334.           Func := UserFuncList.Objects[UserFunc];
  335.           Value := TFarUserFunction(Func)(Calculate);
  336.           if FParseText[FCurPos] <> ')' then InvalidCondition(SParseNotCramp);
  337.           Inc(FCurPos);
  338.         end
  339.         else if GetFunction(NoFunc) then begin
  340.           Inc(FCurPos);
  341.           Value := Calculate;
  342.           try
  343.             case NoFunc of
  344.               pfArcTan: Value := ArcTan(Value);
  345.               pfCos: Value := Cos(Value);
  346.               pfSin: Value := Sin(Value);
  347.               pfTan:
  348.                 if Cos(Value) = 0 then InvalidCondition(SParseDivideByZero)
  349.                 else Value := Sin(Value) / Cos(Value);
  350.               pfAbs: Value := Abs(Value);
  351.               pfExp: Value := Exp(Value);
  352.               pfLn:
  353.                 if Value <= 0 then InvalidCondition(SParseLogError)
  354.                 else Value := Ln(Value);
  355.               pfLog:
  356.                 if Value <= 0 then InvalidCondition(SParseLogError)
  357.                 else Value := Ln(Value) / Ln(10);
  358.               pfSqrt:
  359.                 if Value < 0 then InvalidCondition(SParseSqrError)
  360.                 else Value := Sqrt(Value);
  361.               pfSqr: Value := Sqr(Value);
  362.               pfInt: Value := Round(Value);
  363.               pfFrac: Value := Frac(Value);
  364.               pfTrunc: Value := Trunc(Value);
  365.               pfRound: Value := Round(Value);
  366.               pfArcSin:
  367.                 if Value = 1 then Value := Pi / 2
  368.                 else Value := ArcTan(Value / Sqrt(1 - Sqr(Value)));
  369.               pfArcCos:
  370.                 if Value = 1 then Value := 0
  371.                 else Value := Pi / 2 - ArcTan(Value / Sqrt(1 - Sqr(Value)));
  372.               pfSign:
  373.                 if Value > 0 then Value := 1
  374.                 else if Value < 0 then Value := -1;
  375.               pfNot: Value := not Trunc(Value);
  376.             end;
  377.           except
  378.             on E: ERxParserError do raise
  379.             else InvalidCondition(SParseInvalidFloatOperation);
  380.           end;
  381.           if FParseText[FCurPos] <> ')' then InvalidCondition(SParseNotCramp);
  382.           Inc(FCurPos);
  383.         end
  384.         else InvalidCondition(SParseSyntaxError);
  385.   end;
  386.   Result := Value;
  387. end;
  388.  
  389. function TRxMathParser.SubTerm: Extended;
  390. var
  391.   Value: Extended;
  392. begin
  393.   Value := Term;
  394.   while FParseText[FCurPos] in ['*', '^', '/'] do begin
  395.     Inc(FCurPos);
  396.     if FParseText[FCurPos - 1] = '*' then
  397.       Value := Value * Term
  398.     else if FParseText[FCurPos - 1] = '^' then
  399.       Value := Power(Value, Term)
  400.     else if FParseText[FCurPos - 1] = '/' then
  401.       try
  402.         Value := Value / Term;
  403.       except
  404.         InvalidCondition(SParseDivideByZero);
  405.       end;
  406.   end;
  407.   Result := Value;
  408. end;
  409.  
  410. function TRxMathParser.Calculate: Extended;
  411. var
  412.   Value: Extended;
  413. begin
  414.   Value := SubTerm;
  415.   while FParseText[FCurPos] in ['+', '-'] do begin
  416.     Inc(FCurPos);
  417.     if FParseText[FCurPos - 1] = '+' then Value := Value + SubTerm
  418.     else Value := Value - SubTerm;
  419.   end;
  420.   if not (FParseText[FCurPos] in [#0, ')', '>', '<', '=', ',']) then
  421.     InvalidCondition(SParseSyntaxError);
  422.   Result := Value;
  423. end;
  424.  
  425. function TRxMathParser.Exec(const AFormula: string): Extended;
  426. var
  427.   I, J: Integer;
  428. begin
  429.   J := 0;
  430.   Result := 0;
  431.   FParseText := '';
  432.   for I := 1 to Length(AFormula) do begin
  433.     case AFormula[I] of
  434.       '(': Inc(J);
  435.       ')': Dec(J);
  436.     end;
  437.     if AFormula[I] > ' ' then FParseText := FParseText + UpCase(AFormula[I]);
  438.   end;
  439.   if J = 0 then begin
  440.     FCurPos := 1;
  441.     FParseText := FParseText + #0;
  442.     if (FParseText[1] in ['-', '+']) then FParseText := '0' + FParseText;
  443.     Result := Calculate;
  444.   end
  445.   else InvalidCondition(SParseNotCramp);
  446. end;
  447.  
  448. class procedure TRxMathParser.RegisterUserFunction(const Name: string;
  449.   Proc: TUserFunction);
  450. var
  451.   I: Integer;
  452. begin
  453.   if (Length(Name) > 0) and (Name[1] in ['A'..'Z', 'a'..'z', '_']) then
  454.   begin
  455.     if not Assigned(Proc) then UnregisterUserFunction(Name)
  456.     else begin
  457.       with GetUserFuncList do begin
  458.         I := IndexOf(Name);
  459.         if I < 0 then I := Add(Name);
  460. {$IFDEF WIN32}
  461.         Objects[I] := @Proc;
  462. {$ELSE}
  463.         Objects[I] := Proc;
  464. {$ENDIF}
  465.       end;
  466.     end;
  467.   end
  468.   else InvalidCondition(SParseSyntaxError);
  469. end;
  470.  
  471. class procedure TRxMathParser.UnregisterUserFunction(const Name: string);
  472. var
  473.   I: Integer;
  474. begin
  475.   if Assigned(UserFuncList) then
  476.     with UserFuncList do begin
  477.       I := IndexOf(Name);
  478.       if I >= 0 then Delete(I);
  479.       if Count = 0 then FreeUserFunc;
  480.     end;
  481. end;
  482.  
  483. initialization
  484.   UserFuncList := nil;
  485. {$IFDEF WIN32}
  486. finalization
  487.   FreeUserFunc;  
  488. {$ELSE}
  489.   AddExitProc(FreeUserFunc);
  490. {$ENDIF}
  491. end.
  492.