home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 June / Chip_2002-06_cd1.bin / zkuste / delphi / kolekce / d6 / rxlibsetup.exe / {app} / units / PARSING.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  2002-02-19  |  13.3 KB  |  494 lines

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