git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.jvcl@12 7f62d464-2af8-f54e-996c-e91b33f51cbe
603 lines
15 KiB
ObjectPascal
603 lines
15 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvParsing.PAS, released on 2002-07-04.
|
|
|
|
The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 2001,2002 SGB Software
|
|
All Rights Reserved.
|
|
|
|
Last Modified: 2002-07-04
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Known Issues:
|
|
-----------------------------------------------------------------------------}
|
|
|
|
{$I JVCL.INC}
|
|
|
|
unit JvParsing;
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Classes,
|
|
JvTypes;
|
|
|
|
type
|
|
TParserFunc = (pfArcTan, pfCos, pfSin, pfTan, pfAbs, pfExp, pfLn, pfLog,
|
|
pfSqrt, pfSqr, pfInt, pfFrac, pfTrunc, pfRound, pfArcSin, pfArcCos,
|
|
pfSign, pfNot);
|
|
EJvParserError = class(EJVCLException);
|
|
{$IFDEF WIN32}
|
|
TUserFunction = function(Value: Extended): Extended;
|
|
{$ELSE}
|
|
TUserFunction = Pointer;
|
|
{$ENDIF}
|
|
|
|
TJvMathParser = class(TObject)
|
|
private
|
|
FCurPos: Cardinal;
|
|
FParseText: string;
|
|
function GetChar: Char;
|
|
procedure NextChar;
|
|
function GetNumber(var AValue: Extended): Boolean;
|
|
function GetConst(var AValue: Extended): Boolean;
|
|
function GetFunction(var AValue: TParserFunc): Boolean;
|
|
function GetUserFunction(var Index: Integer): Boolean;
|
|
function Term: Extended;
|
|
function SubTerm: Extended;
|
|
function Calculate: Extended;
|
|
public
|
|
// (rom) renamed from Exec
|
|
function Execute(const AFormula: string): Extended;
|
|
class procedure RegisterUserFunction(const Name: string; Proc: TUserFunction);
|
|
class procedure UnregisterUserFunction(const Name: string);
|
|
end;
|
|
|
|
function GetFormulaValue(const Formula: string): Extended;
|
|
|
|
{$IFNDEF WIN32}
|
|
function Power(Base, Exponent: Extended): Extended;
|
|
{$ENDIF}
|
|
|
|
implementation
|
|
|
|
uses
|
|
JvxRConst;
|
|
|
|
const
|
|
cSpecialChars = [#0..' ', '+', '-', '/', '*', ')', '^'];
|
|
cIdentifierChars = ['A'..'Z', 'a'..'z', '_'];
|
|
|
|
FuncNames: array [TParserFunc] of PChar =
|
|
('ARCTAN', 'COS', 'SIN', 'TAN', 'ABS', 'EXP', 'LN', 'LOG',
|
|
'SQRT', 'SQR', 'INT', 'FRAC', 'TRUNC', 'ROUND', 'ARCSIN', 'ARCCOS',
|
|
'SIGN', 'NOT');
|
|
|
|
{ Parser errors }
|
|
|
|
procedure InvalidCondition(const Str: string);
|
|
begin
|
|
raise EJvParserError.Create(Str);
|
|
end;
|
|
|
|
{ IntPower and Power functions are copied from Borland's MATH.PAS unit }
|
|
|
|
{$IFDEF WIN32}
|
|
function IntPower(Base: Extended; Exponent: Integer): Extended;
|
|
asm
|
|
mov ecx, eax
|
|
cdq
|
|
fld1 { Result := 1 }
|
|
xor eax, edx
|
|
sub eax, edx { eax := Abs(Exponent) }
|
|
jz @@3
|
|
fld Base
|
|
jmp @@2
|
|
@@1: fmul ST, ST { X := Base * Base }
|
|
@@2: shr eax,1
|
|
jnc @@1
|
|
fmul ST(1),ST { Result := Result * X }
|
|
jnz @@1
|
|
fstp st { pop X from FPU stack }
|
|
cmp ecx, 0
|
|
jge @@3
|
|
fld1
|
|
fdivrp { Result := 1 / Result }
|
|
@@3:
|
|
fwait
|
|
end;
|
|
{$ELSE}
|
|
function IntPower(Base: Extended; Exponent: Integer): Extended;
|
|
var
|
|
Y: Longint;
|
|
begin
|
|
Y := Abs(Exponent);
|
|
Result := 1.0;
|
|
while Y > 0 do
|
|
begin
|
|
while not Odd(Y) do
|
|
begin
|
|
Y := Y shr 1;
|
|
Base := Base * Base;
|
|
end;
|
|
Dec(Y);
|
|
Result := Result * Base;
|
|
end;
|
|
if Exponent < 0 then
|
|
Result := 1.0 / Result;
|
|
end;
|
|
{$ENDIF WIN32}
|
|
|
|
function Power(Base, Exponent: Extended): Extended;
|
|
begin
|
|
if Exponent = 0.0 then
|
|
Result := 1.0
|
|
else
|
|
if (Base = 0.0) and (Exponent > 0.0) then
|
|
Result := 0.0
|
|
else
|
|
if (Frac(Exponent) = 0.0) and (Abs(Exponent) <= MaxInt) then
|
|
Result := IntPower(Base, Trunc(Exponent))
|
|
else
|
|
Result := Exp(Exponent * Ln(Base))
|
|
end;
|
|
|
|
{ User defined functions }
|
|
|
|
type
|
|
{$IFDEF WIN32}
|
|
TFarUserFunction = TUserFunction;
|
|
{$ELSE}
|
|
TFarUserFunction = function(Value: Extended): Extended;
|
|
{$ENDIF}
|
|
|
|
var
|
|
UserFuncList: TStrings;
|
|
|
|
function GetUserFuncList: TStrings;
|
|
begin
|
|
if not Assigned(UserFuncList) then
|
|
begin
|
|
UserFuncList := TStringList.Create;
|
|
with TStringList(UserFuncList) do
|
|
begin
|
|
Sorted := True;
|
|
Duplicates := dupIgnore;
|
|
end;
|
|
end;
|
|
Result := UserFuncList;
|
|
end;
|
|
|
|
procedure FreeUserFunc; far;
|
|
begin
|
|
UserFuncList.Free;
|
|
UserFuncList := nil;
|
|
end;
|
|
|
|
{ Parsing routines }
|
|
|
|
function GetFormulaValue(const Formula: string): Extended;
|
|
begin
|
|
with TJvMathParser.Create do
|
|
try
|
|
Result := Execute(Formula);
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
function TJvMathParser.GetChar: Char;
|
|
begin
|
|
Result := FParseText[FCurPos];
|
|
end;
|
|
|
|
procedure TJvMathParser.NextChar;
|
|
begin
|
|
Inc(FCurPos);
|
|
end;
|
|
|
|
function TJvMathParser.GetNumber(var AValue: Extended): Boolean;
|
|
var
|
|
C: Char;
|
|
SavePos: Cardinal;
|
|
Code: Integer;
|
|
IsHex: Boolean;
|
|
TmpStr: string;
|
|
begin
|
|
Result := False;
|
|
C := GetChar;
|
|
SavePos := FCurPos;
|
|
TmpStr := '';
|
|
IsHex := False;
|
|
if C = '$' then
|
|
begin
|
|
TmpStr := C;
|
|
NextChar;
|
|
C := GetChar;
|
|
while C in ['0'..'9', 'A'..'F', 'a'..'f'] do
|
|
begin
|
|
TmpStr := TmpStr + C;
|
|
NextChar;
|
|
C := GetChar;
|
|
end;
|
|
IsHex := True;
|
|
Result := (Length(TmpStr) > 1) and (Length(TmpStr) <= 9);
|
|
end
|
|
else
|
|
if C in ['+', '-', '0'..'9', '.', DecimalSeparator] then
|
|
begin
|
|
if (C in ['.', DecimalSeparator]) then
|
|
TmpStr := '0' + '.'
|
|
else
|
|
TmpStr := C;
|
|
NextChar;
|
|
C := GetChar;
|
|
if (Length(TmpStr) = 1) and (TmpStr[1] in ['+', '-']) and
|
|
(C in ['.', DecimalSeparator]) then
|
|
TmpStr := TmpStr + '0';
|
|
while C in ['0'..'9', '.', 'E', 'e', DecimalSeparator] do
|
|
begin
|
|
if C = DecimalSeparator then
|
|
TmpStr := TmpStr + '.'
|
|
else
|
|
TmpStr := TmpStr + C;
|
|
if (C = 'E') then
|
|
begin
|
|
if (Length(TmpStr) > 1) and (TmpStr[Length(TmpStr) - 1] = '.') then
|
|
Insert('0', TmpStr, Length(TmpStr));
|
|
NextChar;
|
|
C := GetChar;
|
|
if (C in ['+', '-']) then
|
|
begin
|
|
TmpStr := TmpStr + C;
|
|
NextChar;
|
|
end;
|
|
end
|
|
else
|
|
NextChar;
|
|
C := GetChar;
|
|
end;
|
|
if (TmpStr[Length(TmpStr)] = '.') and (Pos('E', TmpStr) = 0) then
|
|
TmpStr := TmpStr + '0';
|
|
Val(TmpStr, AValue, Code);
|
|
Result := (Code = 0);
|
|
end;
|
|
Result := Result and (FParseText[FCurPos] in cSpecialChars);
|
|
if Result then
|
|
begin
|
|
if IsHex then
|
|
AValue := StrToInt(TmpStr);
|
|
{ else AValue := StrToFloat(TmpStr) };
|
|
end
|
|
else
|
|
begin
|
|
AValue := 0;
|
|
FCurPos := SavePos;
|
|
end;
|
|
end;
|
|
|
|
function TJvMathParser.GetConst(var AValue: Extended): Boolean;
|
|
begin
|
|
Result := False;
|
|
case FParseText[FCurPos] of
|
|
'E':
|
|
if FParseText[FCurPos + 1] in cSpecialChars then
|
|
begin
|
|
AValue := Exp(1);
|
|
Inc(FCurPos);
|
|
Result := True;
|
|
end;
|
|
'P':
|
|
if (FParseText[FCurPos + 1] = 'I') and
|
|
(FParseText[FCurPos + 2] in cSpecialChars) then
|
|
begin
|
|
AValue := Pi;
|
|
Inc(FCurPos, 2);
|
|
Result := True;
|
|
end;
|
|
end
|
|
end;
|
|
|
|
function TJvMathParser.GetUserFunction(var Index: Integer): Boolean;
|
|
var
|
|
TmpStr: string;
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
if (FParseText[FCurPos] in cIdentifierChars) and
|
|
Assigned(UserFuncList) then
|
|
begin
|
|
with UserFuncList do
|
|
for I := 0 to Count - 1 do
|
|
begin
|
|
TmpStr := Copy(FParseText, FCurPos, Length(Strings[I]));
|
|
if (CompareText(TmpStr, Strings[I]) = 0) and
|
|
(Objects[I] <> nil) then
|
|
begin
|
|
if FParseText[FCurPos + Cardinal(Length(TmpStr))] = '(' then
|
|
begin
|
|
Result := True;
|
|
Inc(FCurPos, Length(TmpStr));
|
|
Index := I;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Index := -1;
|
|
end;
|
|
|
|
function TJvMathParser.GetFunction(var AValue: TParserFunc): Boolean;
|
|
var
|
|
I: TParserFunc;
|
|
TmpStr: string;
|
|
begin
|
|
Result := False;
|
|
AValue := Low(TParserFunc);
|
|
if FParseText[FCurPos] in cIdentifierChars then
|
|
begin
|
|
for I := Low(TParserFunc) to High(TParserFunc) do
|
|
begin
|
|
TmpStr := Copy(FParseText, FCurPos, StrLen(FuncNames[I]));
|
|
if CompareText(TmpStr, StrPas(FuncNames[I])) = 0 then
|
|
begin
|
|
AValue := I;
|
|
if FParseText[FCurPos + Cardinal(Length(TmpStr))] = '(' then
|
|
begin
|
|
Result := True;
|
|
Inc(FCurPos, Length(TmpStr));
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJvMathParser.Term: Extended;
|
|
var
|
|
Value: Extended;
|
|
NoFunc: TParserFunc;
|
|
UserFunc: Integer;
|
|
Func: Pointer;
|
|
begin
|
|
if FParseText[FCurPos] = '(' then
|
|
begin
|
|
Inc(FCurPos);
|
|
Value := Calculate;
|
|
if FParseText[FCurPos] <> ')' then
|
|
InvalidCondition(SParseNotCramp);
|
|
Inc(FCurPos);
|
|
end
|
|
else
|
|
begin
|
|
if not GetNumber(Value) then
|
|
if not GetConst(Value) then
|
|
if GetUserFunction(UserFunc) then
|
|
begin
|
|
Inc(FCurPos);
|
|
Func := UserFuncList.Objects[UserFunc];
|
|
Value := TFarUserFunction(Func)(Calculate);
|
|
if FParseText[FCurPos] <> ')' then
|
|
InvalidCondition(SParseNotCramp);
|
|
Inc(FCurPos);
|
|
end
|
|
else
|
|
if GetFunction(NoFunc) then
|
|
begin
|
|
Inc(FCurPos);
|
|
Value := Calculate;
|
|
try
|
|
case NoFunc of
|
|
pfArcTan:
|
|
Value := ArcTan(Value);
|
|
pfCos:
|
|
Value := Cos(Value);
|
|
pfSin:
|
|
Value := Sin(Value);
|
|
pfTan:
|
|
if Cos(Value) = 0 then
|
|
InvalidCondition(SParseDivideByZero)
|
|
else
|
|
Value := Sin(Value) / Cos(Value);
|
|
pfAbs:
|
|
Value := Abs(Value);
|
|
pfExp:
|
|
Value := Exp(Value);
|
|
pfLn:
|
|
if Value <= 0 then
|
|
InvalidCondition(SParseLogError)
|
|
else
|
|
Value := Ln(Value);
|
|
pfLog:
|
|
if Value <= 0 then
|
|
InvalidCondition(SParseLogError)
|
|
else
|
|
Value := Ln(Value) / Ln(10);
|
|
pfSqrt:
|
|
if Value < 0 then
|
|
InvalidCondition(SParseSqrError)
|
|
else
|
|
Value := Sqrt(Value);
|
|
pfSqr:
|
|
Value := Sqr(Value);
|
|
pfInt:
|
|
Value := Round(Value);
|
|
pfFrac:
|
|
Value := Frac(Value);
|
|
pfTrunc:
|
|
Value := Trunc(Value);
|
|
pfRound:
|
|
Value := Round(Value);
|
|
pfArcSin:
|
|
if Value = 1 then
|
|
Value := Pi / 2
|
|
else
|
|
Value := ArcTan(Value / Sqrt(1 - Sqr(Value)));
|
|
pfArcCos:
|
|
if Value = 1 then
|
|
Value := 0
|
|
else
|
|
Value := Pi / 2 - ArcTan(Value / Sqrt(1 - Sqr(Value)));
|
|
pfSign:
|
|
if Value > 0 then
|
|
Value := 1
|
|
else
|
|
if Value < 0 then
|
|
Value := -1;
|
|
pfNot:
|
|
Value := not Trunc(Value);
|
|
end;
|
|
except
|
|
on E: EJvParserError do
|
|
raise
|
|
else
|
|
InvalidCondition(SParseInvalidFloatOperation);
|
|
end;
|
|
if FParseText[FCurPos] <> ')' then
|
|
InvalidCondition(SParseNotCramp);
|
|
Inc(FCurPos);
|
|
end
|
|
else
|
|
InvalidCondition(SParseSyntaxError);
|
|
end;
|
|
Result := Value;
|
|
end;
|
|
|
|
function TJvMathParser.SubTerm: Extended;
|
|
var
|
|
Value: Extended;
|
|
begin
|
|
Value := Term;
|
|
while FParseText[FCurPos] in ['*', '^', '/'] do
|
|
begin
|
|
Inc(FCurPos);
|
|
if FParseText[FCurPos - 1] = '*' then
|
|
Value := Value * Term
|
|
else
|
|
if FParseText[FCurPos - 1] = '^' then
|
|
Value := Power(Value, Term)
|
|
else
|
|
if FParseText[FCurPos - 1] = '/' then
|
|
try
|
|
Value := Value / Term;
|
|
except
|
|
InvalidCondition(SParseDivideByZero);
|
|
end;
|
|
end;
|
|
Result := Value;
|
|
end;
|
|
|
|
function TJvMathParser.Calculate: Extended;
|
|
var
|
|
Value: Extended;
|
|
begin
|
|
Value := SubTerm;
|
|
while FParseText[FCurPos] in ['+', '-'] do
|
|
begin
|
|
Inc(FCurPos);
|
|
if FParseText[FCurPos - 1] = '+' then
|
|
Value := Value + SubTerm
|
|
else
|
|
Value := Value - SubTerm;
|
|
end;
|
|
if not (FParseText[FCurPos] in [#0, ')', '>', '<', '=', ',']) then
|
|
InvalidCondition(SParseSyntaxError);
|
|
Result := Value;
|
|
end;
|
|
|
|
function TJvMathParser.Execute(const AFormula: string): Extended;
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
J := 0;
|
|
Result := 0;
|
|
FParseText := '';
|
|
for I := 1 to Length(AFormula) do
|
|
begin
|
|
case AFormula[I] of
|
|
'(':
|
|
Inc(J);
|
|
')':
|
|
Dec(J);
|
|
end;
|
|
if AFormula[I] > ' ' then
|
|
FParseText := FParseText + UpCase(AFormula[I]);
|
|
end;
|
|
if J = 0 then
|
|
begin
|
|
FCurPos := 1;
|
|
FParseText := FParseText + #0;
|
|
if (FParseText[1] in ['-', '+']) then
|
|
FParseText := '0' + FParseText;
|
|
Result := Calculate;
|
|
end
|
|
else
|
|
InvalidCondition(SParseNotCramp);
|
|
end;
|
|
|
|
class procedure TJvMathParser.RegisterUserFunction(const Name: string;
|
|
Proc: TUserFunction);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if (Length(Name) > 0) and (Name[1] in cIdentifierChars) then
|
|
begin
|
|
if not Assigned(Proc) then
|
|
UnregisterUserFunction(Name)
|
|
else
|
|
begin
|
|
with GetUserFuncList do
|
|
begin
|
|
I := IndexOf(Name);
|
|
if I < 0 then
|
|
I := Add(Name);
|
|
{$IFDEF WIN32}
|
|
Objects[I] := @Proc;
|
|
{$ELSE}
|
|
Objects[I] := Proc;
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
InvalidCondition(SParseSyntaxError);
|
|
end;
|
|
|
|
class procedure TJvMathParser.UnregisterUserFunction(const Name: string);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if Assigned(UserFuncList) then
|
|
with UserFuncList do
|
|
begin
|
|
I := IndexOf(Name);
|
|
if I >= 0 then
|
|
Delete(I);
|
|
if Count = 0 then
|
|
FreeUserFunc;
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
UserFuncList := nil;
|
|
{$IFDEF WIN32}
|
|
finalization
|
|
FreeUserFunc;
|
|
{$ELSE}
|
|
AddExitProc(FreeUserFunc);
|
|
{$ENDIF}
|
|
|
|
end.
|
|
|