📄 jvparsing.pas
字号:
{-----------------------------------------------------------------------------
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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -