720 lines
15 KiB
Plaintext
720 lines
15 KiB
Plaintext
unit ParseClass;
|
|
|
|
interface
|
|
|
|
uses OObjects, SysUtils;
|
|
|
|
const
|
|
MaxArg = 6;
|
|
|
|
const
|
|
Nan: Double = 0 / 0;
|
|
function isNan(const d: Double): boolean;
|
|
|
|
type
|
|
TVarType = (vtDouble, vtBoolean, vtString, vtLeftBracket,
|
|
vtRightBracket, vtComma);
|
|
PDouble = ^Double;
|
|
EParserException = class(Exception);
|
|
PExpressionRec = ^TExpressionRec;
|
|
|
|
TExprWord = class;
|
|
|
|
TArgsArray = record
|
|
Res: Double;
|
|
Args: array [0 .. MaxArg - 1] of PDouble;
|
|
ExprWord: TExprWord; // can be used to notify the object to update
|
|
end;
|
|
|
|
TDoubleFunc = procedure(Expr: PExpressionRec);
|
|
|
|
TStringFunc = function(s1, s2: string): Double;
|
|
|
|
TExpressionRec = record
|
|
// used both as linked tree and linked list for maximum evaluation efficiency
|
|
Oper: TDoubleFunc;
|
|
Next: PExpressionRec;
|
|
Res: Double;
|
|
ExprWord: TExprWord;
|
|
case Byte of
|
|
0:
|
|
(Args: array [0 .. MaxArg - 1] of PDouble;
|
|
// can be used to notify the object to update
|
|
);
|
|
1:
|
|
(ArgList: array [0 .. MaxArg - 1] of PExpressionRec);
|
|
end;
|
|
|
|
TExprCollection = class(TNoOwnerCollection)
|
|
public
|
|
function NextOper(IStart: Integer): Integer;
|
|
procedure Check;
|
|
procedure EraseExtraBrackets;
|
|
end;
|
|
|
|
TExprWord = class
|
|
private
|
|
FName: string;
|
|
FDoubleFunc: TDoubleFunc;
|
|
protected
|
|
function GetIsOper: boolean; virtual;
|
|
function GetAsString: string; virtual;
|
|
function GetIsVariable: boolean;
|
|
function GetCanVary: boolean; virtual;
|
|
function GetVarType: TVarType; virtual;
|
|
function GetNFunctionArg: Integer; virtual;
|
|
function GetDescription: string; virtual;
|
|
public
|
|
constructor Create(AName: string; ADoubleFunc: TDoubleFunc);
|
|
function AsPointer: PDouble; virtual;
|
|
property AsString: string read GetAsString;
|
|
property DoubleFunc: TDoubleFunc read FDoubleFunc;
|
|
property IsOper: boolean read GetIsOper;
|
|
property CanVary: boolean read GetCanVary;
|
|
property isVariable: boolean read GetIsVariable;
|
|
property VarType: TVarType read GetVarType;
|
|
property NFunctionArg: Integer read GetNFunctionArg;
|
|
property Name: string read FName;
|
|
property Description: string read GetDescription;
|
|
end;
|
|
|
|
TExpressList = class(TSortedCollection)
|
|
public
|
|
function KeyOf(Item: Pointer): Pointer; override;
|
|
function Compare(Key1, Key2: Pointer): Integer; override;
|
|
end;
|
|
|
|
TDoubleConstant = class(TExprWord)
|
|
private
|
|
FValue: Double;
|
|
public
|
|
function AsPointer: PDouble; override;
|
|
constructor Create(AName: string; AValue: string);
|
|
constructor CreateAsDouble(AName: string; AValue: Double);
|
|
// not overloaded to support older Delphi versions
|
|
property Value: Double read FValue write FValue;
|
|
end;
|
|
|
|
TConstant = class(TDoubleConstant)
|
|
private
|
|
FDescription: string;
|
|
protected
|
|
function GetDescription: string; override;
|
|
public
|
|
constructor CreateAsDouble(AName, Descr: string; AValue: Double);
|
|
end;
|
|
|
|
TBooleanConstant = class(TDoubleConstant)
|
|
protected
|
|
function GetVarType: TVarType; override;
|
|
end;
|
|
|
|
TGeneratedVariable = class(TDoubleConstant)
|
|
private
|
|
FAsString: string;
|
|
FVarType: TVarType;
|
|
protected
|
|
function GetVarType: TVarType; override;
|
|
function GetAsString: string; override;
|
|
function GetCanVary: boolean; override;
|
|
public
|
|
constructor Create(AName: string);
|
|
property VarType read GetVarType write FVarType;
|
|
property AsString: string read GetAsString write FAsString;
|
|
end;
|
|
|
|
TDoubleVariable = class(TExprWord)
|
|
private
|
|
FValue: PDouble;
|
|
protected
|
|
function GetCanVary: boolean; override;
|
|
public
|
|
function AsPointer: PDouble; override;
|
|
constructor Create(AName: string; AValue: PDouble);
|
|
end;
|
|
|
|
TStringConstant = class(TExprWord)
|
|
private
|
|
FValue: string;
|
|
protected
|
|
function GetVarType: TVarType; override;
|
|
function GetAsString: string; override;
|
|
public
|
|
constructor Create(AValue: string);
|
|
end;
|
|
|
|
TLeftBracket = class(TExprWord)
|
|
function GetVarType: TVarType; override;
|
|
end;
|
|
|
|
TRightBracket = class(TExprWord)
|
|
protected
|
|
function GetVarType: TVarType; override;
|
|
end;
|
|
|
|
TComma = class(TExprWord)
|
|
protected
|
|
function GetVarType: TVarType; override;
|
|
end;
|
|
|
|
PString = ^string;
|
|
|
|
TStringVariable = class(TExprWord)
|
|
private
|
|
FValue: PString;
|
|
protected
|
|
function GetVarType: TVarType; override;
|
|
function GetAsString: string; override;
|
|
function GetCanVary: boolean; override;
|
|
public
|
|
constructor Create(AName: string; AValue: PString);
|
|
end;
|
|
|
|
TFunction = class(TExprWord)
|
|
private
|
|
FIsOper: boolean;
|
|
FOperPrec: Integer;
|
|
FNFunctionArg: Integer;
|
|
FDescription: string;
|
|
protected
|
|
function GetDescription: string; override;
|
|
function GetIsOper: boolean; override;
|
|
function GetNFunctionArg: Integer; override;
|
|
public
|
|
constructor Create(AName, Descr: string; ADoubleFunc: TDoubleFunc;
|
|
ANFunctionArg: Integer);
|
|
constructor CreateOper(AName: string; ADoubleFunc: TDoubleFunc;
|
|
ANFunctionArg: Integer; AIsOper: boolean; AOperPrec: Integer);
|
|
property OperPrec: Integer read FOperPrec;
|
|
end;
|
|
|
|
TVaryingFunction = class(TFunction)
|
|
// Functions that can vary for ex. random generators
|
|
// should be TVaryingFunction to be sure that they are
|
|
// always evaluated
|
|
protected
|
|
function GetCanVary: boolean; override;
|
|
end;
|
|
|
|
TBooleanFunction = class(TFunction)
|
|
protected
|
|
function GetVarType: TVarType; override;
|
|
end;
|
|
|
|
TOper = (op_eq, op_gt, op_lt, op_ge, op_le, op_in);
|
|
|
|
const
|
|
ListChar = ','; { the delimiter used with the 'in' operator: e.g.,
|
|
('a' in 'a,b') =True
|
|
('c' in 'a,b') =False }
|
|
|
|
type
|
|
TSimpleStringFunction = class(TFunction)
|
|
private
|
|
FStringFunc: TStringFunc;
|
|
FLeftArg: TExprWord;
|
|
FRightArg: TExprWord;
|
|
protected
|
|
function GetCanVary: boolean; override;
|
|
public
|
|
constructor Create(AName, Descr: string; AStringFunc: TStringFunc;
|
|
ALeftArg, ARightArg: TExprWord);
|
|
function Evaluate: Double;
|
|
property StringFunc: TStringFunc read FStringFunc;
|
|
end;
|
|
|
|
TVaryingStringFunction = class(TSimpleStringFunction)
|
|
protected
|
|
function GetCanVary: boolean; override;
|
|
end;
|
|
|
|
TLogicalStringOper = class(TSimpleStringFunction)
|
|
protected
|
|
function GetVarType: TVarType; override;
|
|
public
|
|
constructor Create(AOper: string; ALeftArg: TExprWord;
|
|
ARightArg: TExprWord);
|
|
end;
|
|
|
|
procedure _Variable(Param: PExpressionRec);
|
|
|
|
// procedure _StringFunc(Param: PExpressionRec);
|
|
|
|
implementation
|
|
|
|
// function _StrIn(sLookfor, sData: string): Double;
|
|
|
|
// function _StrInt(a, b: string): Double;
|
|
|
|
function isNan(const d: Double): boolean;
|
|
begin
|
|
Result := comp(d) = comp(Nan);
|
|
// slower alternative: CompareMem(@d, @Nan, SizeOf(Double))
|
|
end;
|
|
|
|
procedure _Variable(Param: PExpressionRec);
|
|
begin
|
|
with Param^ do
|
|
Res := Args[0]^;
|
|
end;
|
|
|
|
procedure _StringFunc(Param: PExpressionRec);
|
|
begin
|
|
with Param^ do
|
|
Res := TSimpleStringFunction(ExprWord).Evaluate;
|
|
end;
|
|
|
|
function _StrInt(a, b: string): Double;
|
|
begin
|
|
Result := StrToInt(a);
|
|
end;
|
|
|
|
function _StrEq(s1, s2: string): Double;
|
|
begin
|
|
Result := Byte(s1 = s2);
|
|
end;
|
|
|
|
function _StrGt(s1, s2: string): Double;
|
|
begin
|
|
Result := Byte(s1 > s2);
|
|
end;
|
|
|
|
function _Strlt(s1, s2: string): Double;
|
|
begin
|
|
Result := Byte(s1 < s2);
|
|
end;
|
|
|
|
function _StrGe(s1, s2: string): Double;
|
|
begin
|
|
Result := Byte(s1 >= s2);
|
|
end;
|
|
|
|
function _Strle(s1, s2: string): Double;
|
|
begin
|
|
Result := Byte(s1 <= s2);
|
|
end;
|
|
|
|
function _Strne(s1, s2: string): Double;
|
|
begin
|
|
Result := Byte(s1 <> s2);
|
|
end;
|
|
|
|
function _StrIn(sLookfor, sData: string): Double;
|
|
var
|
|
loop: Integer;
|
|
subString: string;
|
|
begin
|
|
Result := 0;
|
|
loop := pos(ListChar, sData);
|
|
while loop > 0 do
|
|
begin
|
|
subString := Copy(sData, 1, loop - 1);
|
|
sData := Copy(sData, loop + 1, Length(sData));
|
|
if subString = sLookfor then
|
|
begin
|
|
Result := 1;
|
|
break;
|
|
end;
|
|
loop := pos(ListChar, sData);
|
|
end;
|
|
if sLookfor = sData then
|
|
Result := 1;
|
|
end;
|
|
|
|
{ TExpressionWord }
|
|
|
|
function TExprWord.AsPointer: PDouble;
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
|
|
constructor TExprWord.Create(AName: string; ADoubleFunc: TDoubleFunc);
|
|
begin
|
|
FName := LowerCase(AName);
|
|
FDoubleFunc := ADoubleFunc;
|
|
end;
|
|
|
|
function TExprWord.GetAsString: string;
|
|
begin
|
|
Result := '';
|
|
end;
|
|
|
|
function TExprWord.GetCanVary: boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
function TExprWord.GetDescription: string;
|
|
begin
|
|
Result := '';
|
|
end;
|
|
|
|
function TExprWord.GetIsOper: boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
function TExprWord.GetIsVariable: boolean;
|
|
begin
|
|
Result := @FDoubleFunc = @_Variable
|
|
end;
|
|
|
|
function TExprWord.GetNFunctionArg: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
function TExprWord.GetVarType: TVarType;
|
|
begin
|
|
Result := vtDouble;
|
|
end;
|
|
|
|
{ TDoubleConstant }
|
|
|
|
function TDoubleConstant.AsPointer: PDouble;
|
|
begin
|
|
Result := @FValue;
|
|
end;
|
|
|
|
constructor TDoubleConstant.Create(AName, AValue: string);
|
|
begin
|
|
inherited Create(AName, _Variable);
|
|
if AValue <> '' then
|
|
FValue := StrToFloat(AValue)
|
|
else
|
|
FValue := Nan;
|
|
end;
|
|
|
|
constructor TDoubleConstant.CreateAsDouble(AName: string; AValue: Double);
|
|
begin
|
|
inherited Create(AName, _Variable);
|
|
FValue := AValue;
|
|
end;
|
|
|
|
{ TStringConstant }
|
|
|
|
function TStringConstant.GetAsString: string;
|
|
begin
|
|
Result := FValue;
|
|
end;
|
|
|
|
constructor TStringConstant.Create(AValue: string);
|
|
begin
|
|
inherited Create(AValue, _Variable);
|
|
if (AValue[1] = '''') and (AValue[Length(AValue)] = '''') then
|
|
FValue := Copy(AValue, 2, Length(AValue) - 2)
|
|
else
|
|
FValue := AValue;
|
|
end;
|
|
|
|
function TStringConstant.GetVarType: TVarType;
|
|
begin
|
|
Result := vtString;
|
|
end;
|
|
|
|
{ TDoubleVariable }
|
|
|
|
function TDoubleVariable.AsPointer: PDouble;
|
|
begin
|
|
Result := FValue;
|
|
end;
|
|
|
|
constructor TDoubleVariable.Create(AName: string; AValue: PDouble);
|
|
begin
|
|
inherited Create(AName, _Variable);
|
|
FValue := AValue;
|
|
end;
|
|
|
|
function TDoubleVariable.GetCanVary: boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
{ TFunction }
|
|
|
|
constructor TFunction.Create(AName, Descr: string; ADoubleFunc: TDoubleFunc;
|
|
ANFunctionArg: Integer);
|
|
begin
|
|
FDescription := Descr;
|
|
CreateOper(AName, ADoubleFunc, ANFunctionArg, False, 0);
|
|
// to increase compatibility don't use default parameters
|
|
end;
|
|
|
|
constructor TFunction.CreateOper(AName: string; ADoubleFunc: TDoubleFunc;
|
|
ANFunctionArg: Integer; AIsOper: boolean; AOperPrec: Integer);
|
|
begin
|
|
inherited Create(AName, ADoubleFunc);
|
|
FNFunctionArg := ANFunctionArg;
|
|
if FNFunctionArg > MaxArg then
|
|
raise EParserException.Create('Too many arguments');
|
|
FIsOper := AIsOper;
|
|
FOperPrec := AOperPrec;
|
|
end;
|
|
|
|
function TFunction.GetDescription: string;
|
|
begin
|
|
Result := FDescription;
|
|
end;
|
|
|
|
function TFunction.GetIsOper: boolean;
|
|
begin
|
|
Result := FIsOper;
|
|
end;
|
|
|
|
function TFunction.GetNFunctionArg: Integer;
|
|
begin
|
|
Result := FNFunctionArg;
|
|
end;
|
|
|
|
{ TLeftBracket }
|
|
|
|
function TLeftBracket.GetVarType: TVarType;
|
|
begin
|
|
Result := vtLeftBracket;
|
|
end;
|
|
|
|
{ TExpressList }
|
|
|
|
function TExpressList.Compare(Key1, Key2: Pointer): Integer;
|
|
begin
|
|
Result := StrIComp(Pchar(Key1), Pchar(Key2));
|
|
end;
|
|
|
|
function TExpressList.KeyOf(Item: Pointer): Pointer;
|
|
begin
|
|
Result := Pchar(TExprWord(Item).Name);
|
|
end;
|
|
|
|
{ TRightBracket }
|
|
|
|
function TRightBracket.GetVarType: TVarType;
|
|
begin
|
|
Result := vtRightBracket;
|
|
end;
|
|
|
|
{ TComma }
|
|
|
|
function TComma.GetVarType: TVarType;
|
|
begin
|
|
Result := vtComma;
|
|
end;
|
|
|
|
{ TExprCollection }
|
|
|
|
procedure TExprCollection.Check;
|
|
var
|
|
brCount, I: Integer;
|
|
begin
|
|
brCount := 0;
|
|
for I := 0 to Count - 1 do
|
|
begin
|
|
case TExprWord(Items[I]).VarType of
|
|
vtLeftBracket:
|
|
Inc(brCount);
|
|
vtRightBracket:
|
|
Dec(brCount);
|
|
end;
|
|
end;
|
|
if brCount <> 0 then
|
|
raise EParserException.Create('Unequal brackets');
|
|
end;
|
|
|
|
procedure TExprCollection.EraseExtraBrackets;
|
|
var
|
|
I: Integer;
|
|
brCount: Integer;
|
|
begin
|
|
if (TExprWord(Items[0]).VarType = vtLeftBracket) then
|
|
begin
|
|
brCount := 1;
|
|
I := 1;
|
|
while (I < Count) and (brCount > 0) do
|
|
begin
|
|
case TExprWord(Items[I]).VarType of
|
|
vtLeftBracket:
|
|
Inc(brCount);
|
|
vtRightBracket:
|
|
Dec(brCount);
|
|
end;
|
|
Inc(I);
|
|
end;
|
|
if (brCount = 0) and (I = Count) and
|
|
(TExprWord(Items[I - 1]).VarType = vtRightBracket) then
|
|
begin
|
|
for I := 0 to Count - 3 do
|
|
Items[I] := Items[I + 1];
|
|
Count := Count - 2;
|
|
EraseExtraBrackets; // Check if there are still too many brackets
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TExprCollection.NextOper(IStart: Integer): Integer;
|
|
var
|
|
brCount: Integer;
|
|
begin
|
|
brCount := 0;
|
|
Result := IStart;
|
|
while (Result < Count) and
|
|
((brCount > 0) or (TExprWord(Items[Result]).NFunctionArg <= 0)) do
|
|
begin
|
|
case TExprWord(Items[Result]).VarType of
|
|
vtLeftBracket:
|
|
Inc(brCount);
|
|
vtRightBracket:
|
|
Dec(brCount);
|
|
end;
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
|
|
{ TStringVariable }
|
|
|
|
function TStringVariable.GetAsString: string;
|
|
begin
|
|
if (FValue^[1] = '''') and (FValue^[Length(FValue^)] = '''') then
|
|
Result := Copy(FValue^, 2, Length(FValue^) - 2)
|
|
else
|
|
Result := FValue^
|
|
end;
|
|
|
|
constructor TStringVariable.Create(AName: string; AValue: PString);
|
|
begin
|
|
inherited Create(AName, _Variable);
|
|
FValue := AValue;
|
|
end;
|
|
|
|
function TStringVariable.GetVarType: TVarType;
|
|
begin
|
|
Result := vtString;
|
|
end;
|
|
|
|
function TStringVariable.GetCanVary: boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
{ TLogicalStringOper }
|
|
|
|
constructor TLogicalStringOper.Create(AOper: string;
|
|
ALeftArg, ARightArg: TExprWord);
|
|
begin
|
|
if AOper = '=' then
|
|
FStringFunc := @_StrEq
|
|
else if AOper = '>' then
|
|
FStringFunc := @_StrGt
|
|
else if AOper = '<' then
|
|
FStringFunc := @_Strlt
|
|
else if AOper = '>=' then
|
|
FStringFunc := @_StrGe
|
|
else if AOper = '<=' then
|
|
FStringFunc := @_Strle
|
|
else if AOper = '<>' then
|
|
FStringFunc := @_Strne
|
|
else if AOper = 'in' then
|
|
FStringFunc := @_StrIn
|
|
else
|
|
raise EParserException.Create(AOper + ' is not a valid string operand');
|
|
inherited Create(AOper, '', StringFunc, ALeftArg, ARightArg);
|
|
end;
|
|
|
|
function TLogicalStringOper.GetVarType: TVarType;
|
|
begin
|
|
Result := vtBoolean;
|
|
end;
|
|
|
|
{ TBooleanFunction }
|
|
|
|
function TBooleanFunction.GetVarType: TVarType;
|
|
begin
|
|
Result := vtBoolean;
|
|
end;
|
|
|
|
{ TGeneratedVariable }
|
|
|
|
constructor TGeneratedVariable.Create(AName: string);
|
|
begin
|
|
inherited Create(AName, '');
|
|
FAsString := '';
|
|
FVarType := vtDouble;
|
|
end;
|
|
|
|
function TGeneratedVariable.GetAsString: string;
|
|
begin
|
|
Result := FAsString;
|
|
end;
|
|
|
|
function TGeneratedVariable.GetCanVary: boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
function TGeneratedVariable.GetVarType: TVarType;
|
|
begin
|
|
Result := FVarType;
|
|
end;
|
|
|
|
{ TVaryingFunction }
|
|
|
|
function TVaryingFunction.GetCanVary: boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
{ TBooleanConstant }
|
|
|
|
function TBooleanConstant.GetVarType: TVarType;
|
|
begin
|
|
Result := vtBoolean;
|
|
end;
|
|
|
|
{ TConstant }
|
|
|
|
constructor TConstant.CreateAsDouble(AName, Descr: string; AValue: Double);
|
|
begin
|
|
FDescription := Descr;
|
|
inherited CreateAsDouble(AName, AValue);
|
|
end;
|
|
|
|
function TConstant.GetDescription: string;
|
|
begin
|
|
Result := FDescription;
|
|
end;
|
|
|
|
{ TSimpleStringFunction }
|
|
|
|
constructor TSimpleStringFunction.Create(AName, Descr: string;
|
|
AStringFunc: TStringFunc; ALeftArg, ARightArg: TExprWord);
|
|
begin
|
|
FStringFunc := @AStringFunc;
|
|
FLeftArg := ALeftArg;
|
|
FRightArg := ARightArg;
|
|
inherited Create(AName, Descr, _StringFunc, 0)
|
|
end;
|
|
|
|
function TSimpleStringFunction.Evaluate: Double;
|
|
var
|
|
s1, s2: string;
|
|
begin
|
|
s1 := FLeftArg.AsString;
|
|
if FRightArg <> nil then
|
|
s2 := FRightArg.AsString
|
|
else
|
|
s2 := '';
|
|
Result := StringFunc(s1, s2);
|
|
end;
|
|
|
|
function TSimpleStringFunction.GetCanVary: boolean;
|
|
begin
|
|
Result := ((FLeftArg <> nil) and FLeftArg.CanVary) or
|
|
((FRightArg <> nil) and FRightArg.CanVary);
|
|
end;
|
|
{ TVaryingStringFunction }
|
|
|
|
function TVaryingStringFunction.GetCanVary: boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
end.
|