lazarus/components/fpdebug/fppascalparser.pas
2017-01-29 21:04:32 +00:00

2919 lines
92 KiB
ObjectPascal

{
---------------------------------------------------------------------------
FpPascalParser.pas - Native Freepascal debugger - Parse pascal expressions
---------------------------------------------------------------------------
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
* *
***************************************************************************
}
unit FpPascalParser;
{$mode objfpc}{$H+}
interface
uses
Classes, sysutils, math, DbgIntfBaseTypes, FpDbgInfo, FpdMemoryTools, FpErrorMessages,
LazLoggerBase, LazClasses;
type
TFpPascalExpressionPart = class;
TFpPascalExpressionPartContainer = class;
TFpPascalExpressionPartWithPrecedence = class;
TFpPascalExpressionPartBracket = class;
TFpPascalExpressionPartOperator = class;
TFpPascalExpressionPartClass = class of TFpPascalExpressionPart;
TFpPascalExpressionPartBracketClass = class of TFpPascalExpressionPartBracket;
TSeparatorType = (ppstComma);
{ TFpPascalExpression }
TFpPascalExpression = class
private
FError: TFpError;
FContext: TFpDbgInfoContext;
FFixPCharIndexAccess: Boolean;
FHasPCharIndexAccess: Boolean;
FTextExpression: String;
FExpressionPart: TFpPascalExpressionPart;
FValid: Boolean;
function GetResultValue: TFpDbgValue;
function GetValid: Boolean;
procedure Parse;
procedure SetError(AMsg: String); // deprecated;
procedure SetError(AnErrorCode: TFpErrorCode; AData: array of const);
function PosFromPChar(APChar: PChar): Integer;
protected
function GetDbgSymbolForIdentifier({%H-}AnIdent: String): TFpDbgValue;
property ExpressionPart: TFpPascalExpressionPart read FExpressionPart;
property Context: TFpDbgInfoContext read FContext;
public
constructor Create(ATextExpression: String; AContext: TFpDbgInfoContext);
destructor Destroy; override;
function DebugDump(AWithResults: Boolean = False): String;
procedure ResetEvaluation;
property Error: TFpError read FError;
property Valid: Boolean read GetValid;
// Set by GetResultValue
property HasPCharIndexAccess: Boolean read FHasPCharIndexAccess;
// handle pchar as string (adjust index)
property FixPCharIndexAccess: Boolean read FFixPCharIndexAccess write FFixPCharIndexAccess;
// ResultValue
// - May be a type, if expression is a type
// - Only valid, as long as the expression is not destroyed
property ResultValue: TFpDbgValue read GetResultValue;
end;
{ TFpPascalExpressionPart }
TFpPascalExpressionPart = class
private
FEndChar: PChar;
FParent: TFpPascalExpressionPartContainer;
FStartChar: PChar;
FExpression: TFpPascalExpression;
FResultValue: TFpDbgValue;
FResultValDone: Boolean;
function GetResultValue: TFpDbgValue;
function GetSurroundingOpenBracket: TFpPascalExpressionPartBracket;
function GetTopParent: TFpPascalExpressionPart;
procedure SetEndChar(AValue: PChar);
procedure SetParent(AValue: TFpPascalExpressionPartContainer);
procedure SetStartChar(AValue: PChar);
procedure SetError(AMsg: String = ''); // deprecated;
procedure SetError(APart: TFpPascalExpressionPart; AMsg: String = ''); // deprecated;
procedure SetError(AnErrorCode: TFpErrorCode; AData: array of const);
protected
function DebugText(AIndent: String; {%H-}AWithResults: Boolean): String; virtual; // Self desc only
function DebugDump(AIndent: String; AWithResults: Boolean): String; virtual;
protected
procedure Init; virtual;
function DoGetIsTypeCast: Boolean; virtual; deprecated;
function DoGetResultValue: TFpDbgValue; virtual;
procedure ResetEvaluation;
Procedure ReplaceInParent(AReplacement: TFpPascalExpressionPart);
procedure DoHandleEndOfExpression; virtual;
function IsValidNextPart(APart: TFpPascalExpressionPart): Boolean; virtual;
function IsValidAfterPart({%H-}APrevPart: TFpPascalExpressionPart): Boolean; virtual;
function MaybeHandlePrevPart({%H-}APrevPart: TFpPascalExpressionPart;
var {%H-}AResult: TFpPascalExpressionPart): Boolean; virtual;
// HasPrecedence: an operator with follows precedence rules: the last operand can be taken by the next operator
function HasPrecedence: Boolean; virtual;
function FindLeftSideOperandByPrecedence({%H-}AnOperator: TFpPascalExpressionPartWithPrecedence):
TFpPascalExpressionPart; virtual;
function CanHaveOperatorAsNext: Boolean; virtual; // True
function HandleSeparator(ASeparatorType: TSeparatorType): Boolean; virtual; // False
property Expression: TFpPascalExpression read FExpression;
public
constructor Create(AExpression: TFpPascalExpression; AStartChar: PChar; AnEndChar: PChar = nil);
destructor Destroy; override;
function HandleNextPart(APart: TFpPascalExpressionPart): TFpPascalExpressionPart; virtual;
procedure HandleEndOfExpression; virtual;
function GetText(AMaxLen: Integer=0): String;
property StartChar: PChar read FStartChar write SetStartChar;
property EndChar: PChar read FEndChar write SetEndChar;
property Parent: TFpPascalExpressionPartContainer read FParent write SetParent;
property TopParent: TFpPascalExpressionPart read GetTopParent; // or self
property SurroundingBracket: TFpPascalExpressionPartBracket read GetSurroundingOpenBracket; // incl self
property ResultValue: TFpDbgValue read GetResultValue;
end;
{ TFpPascalExpressionPartContainer }
TFpPascalExpressionPartContainer = class(TFpPascalExpressionPart)
private
FList: TList;
function GetCount: Integer;
function GetItems(AIndex: Integer): TFpPascalExpressionPart;
function GetLastItem: TFpPascalExpressionPart;
procedure SetItems(AIndex: Integer; AValue: TFpPascalExpressionPart);
procedure SetLastItem(AValue: TFpPascalExpressionPart);
protected
procedure Init; override;
function DebugDump(AIndent: String; AWithResults: Boolean): String; override;
public
destructor Destroy; override;
function Add(APart: TFpPascalExpressionPart): Integer;
function IndexOf(APart: TFpPascalExpressionPart): Integer;
procedure Clear;
property Count: Integer read GetCount;
property Items[AIndex: Integer]: TFpPascalExpressionPart read GetItems write SetItems;
property LastItem: TFpPascalExpressionPart read GetLastItem write SetLastItem;
end;
{ TFpPascalExpressionPartIdentifier }
TFpPascalExpressionPartIdentifier = class(TFpPascalExpressionPartContainer)
protected
function DoGetIsTypeCast: Boolean; override;
function DoGetResultValue: TFpDbgValue; override;
end;
TFpPascalExpressionPartConstant = class(TFpPascalExpressionPartContainer)
end;
{ TFpPascalExpressionPartConstantNumber }
TFpPascalExpressionPartConstantNumber = class(TFpPascalExpressionPartConstant)
protected
function DoGetResultValue: TFpDbgValue; override;
end;
{ TFpPascalExpressionPartConstantNumberFloat }
TFpPascalExpressionPartConstantNumberFloat = class(TFpPascalExpressionPartConstantNumber)
protected
function DoGetResultValue: TFpDbgValue; override;
end;
TFpPascalExpressionPartConstantText = class(TFpPascalExpressionPartConstant)
end;
{ TFpPascalExpressionPartWithPrecedence }
TFpPascalExpressionPartWithPrecedence = class(TFpPascalExpressionPartContainer)
protected
FPrecedence: Integer;
function HasPrecedence: Boolean; override;
public
property Precedence: Integer read FPrecedence;
end;
{ TFpPascalExpressionPartBracket }
TFpPascalExpressionPartBracket = class(TFpPascalExpressionPartWithPrecedence)
// some, but not all bracket expr have precedence
private
FIsClosed: boolean;
FIsClosing: boolean;
FAfterComma: Integer;
function GetAfterComma: Boolean;
protected
procedure Init; override;
function HasPrecedence: Boolean; override;
procedure DoHandleEndOfExpression; override;
function CanHaveOperatorAsNext: Boolean; override;
function HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart; virtual;
procedure SetAfterCommaFlag;
property AfterComma: Boolean read GetAfterComma;
public
procedure CloseBracket;
function HandleNextPart(APart: TFpPascalExpressionPart): TFpPascalExpressionPart; override;
procedure HandleEndOfExpression; override;
property IsClosed: boolean read FIsClosed;
end;
{ TFpPascalExpressionPartRoundBracket }
TFpPascalExpressionPartRoundBracket = class(TFpPascalExpressionPartBracket)
protected
end;
{ TFpPascalExpressionPartBracketSubExpression }
TFpPascalExpressionPartBracketSubExpression = class(TFpPascalExpressionPartRoundBracket)
protected
function HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart; override;
function DoGetResultValue: TFpDbgValue; override;
end;
{ TFpPascalExpressionPartBracketArgumentList }
TFpPascalExpressionPartBracketArgumentList = class(TFpPascalExpressionPartRoundBracket)
// function arguments or type cast // this acts a operator: first element is the function/type
protected
procedure Init; override;
function DoGetResultValue: TFpDbgValue; override;
function DoGetIsTypeCast: Boolean; override;
function IsValidAfterPart(APrevPart: TFpPascalExpressionPart): Boolean; override;
function HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart; override;
function MaybeHandlePrevPart(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean; override;
function HandleSeparator(ASeparatorType: TSeparatorType): Boolean; override;
end;
{ TFpPascalExpressionPartSquareBracket }
TFpPascalExpressionPartSquareBracket = class(TFpPascalExpressionPartBracket)
end;
{ TFpPascalExpressionPartBracketSet }
TFpPascalExpressionPartBracketSet = class(TFpPascalExpressionPartSquareBracket)
// a in [x, y, z]
protected
function HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart; override;
function HandleSeparator(ASeparatorType: TSeparatorType): Boolean; override;
end;
{ TFpPascalExpressionPartBracketIndex }
TFpPascalExpressionPartBracketIndex = class(TFpPascalExpressionPartSquareBracket)
// array[1]
protected
procedure Init; override;
function DoGetResultValue: TFpDbgValue; override;
function IsValidAfterPart(APrevPart: TFpPascalExpressionPart): Boolean; override;
function HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart; override;
function MaybeHandlePrevPart(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean; override;
procedure DoHandleEndOfExpression; override;
function HandleSeparator(ASeparatorType: TSeparatorType): Boolean; override;
end;
{ TFpPascalExpressionPartOperator }
TFpPascalExpressionPartOperator = class(TFpPascalExpressionPartWithPrecedence)
protected
function DebugText(AIndent: String; AWithResults: Boolean): String; override;
function CanHaveOperatorAsNext: Boolean; override;
function FindLeftSideOperandByPrecedence(AnOperator: TFpPascalExpressionPartWithPrecedence):
TFpPascalExpressionPart; override;
function HasAllOperands: Boolean; virtual; abstract;
function MaybeAddLeftOperand(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean;
procedure DoHandleEndOfExpression; override;
function HandleSeparator(ASeparatorType: TSeparatorType): Boolean; override;
public
function HandleNextPart(APart: TFpPascalExpressionPart): TFpPascalExpressionPart; override;
end;
{ TFpPascalExpressionPartUnaryOperator }
TFpPascalExpressionPartUnaryOperator = class(TFpPascalExpressionPartOperator)
protected
function HasAllOperands: Boolean; override;
public
end;
{ TFpPascalExpressionPartBinaryOperator }
TFpPascalExpressionPartBinaryOperator = class(TFpPascalExpressionPartOperator)
protected
function HasAllOperands: Boolean; override;
function IsValidAfterPart(APrevPart: TFpPascalExpressionPart): Boolean; override;
public
function MaybeHandlePrevPart(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean; override;
end;
{ TFpPascalExpressionPartOperatorAddressOf }
TFpPascalExpressionPartOperatorAddressOf = class(TFpPascalExpressionPartUnaryOperator) // @
protected
procedure Init; override;
function DoGetResultValue: TFpDbgValue; override;
end;
{ TFpPascalExpressionPartOperatorMakeRef }
TFpPascalExpressionPartOperatorMakeRef = class(TFpPascalExpressionPartUnaryOperator) // ^TTYpe
protected
procedure Init; override;
function IsValidNextPart(APart: TFpPascalExpressionPart): Boolean; override;
function DoGetResultValue: TFpDbgValue; override;
function DoGetIsTypeCast: Boolean; override;
end;
{ TFpPascalExpressionPartOperatorDeRef }
TFpPascalExpressionPartOperatorDeRef = class(TFpPascalExpressionPartUnaryOperator) // ptrval^
protected
procedure Init; override;
function DoGetResultValue: TFpDbgValue; override;
function MaybeHandlePrevPart(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean; override;
function FindLeftSideOperandByPrecedence({%H-}AnOperator: TFpPascalExpressionPartWithPrecedence):
TFpPascalExpressionPart;
override;
// IsValidAfterPart: same as binary op
function IsValidAfterPart(APrevPart: TFpPascalExpressionPart): Boolean; override;
end;
{ TFpPascalExpressionPartOperatorUnaryPlusMinus }
TFpPascalExpressionPartOperatorUnaryPlusMinus = class(TFpPascalExpressionPartUnaryOperator) // + -
// Unary + -
protected
procedure Init; override;
function DoGetResultValue: TFpDbgValue; override;
end;
{ TFpPascalExpressionPartOperatorPlusMinus }
TFpPascalExpressionPartOperatorPlusMinus = class(TFpPascalExpressionPartBinaryOperator) // + -
// Binary + -
protected
procedure Init; override;
function DoGetResultValue: TFpDbgValue; override;
end;
{ TFpPascalExpressionPartOperatorMulDiv }
TFpPascalExpressionPartOperatorMulDiv = class(TFpPascalExpressionPartBinaryOperator) // * /
protected
procedure Init; override;
function DoGetResultValue: TFpDbgValue; override;
end;
{ TFpPascalExpressionPartOperatorCompare }
TFpPascalExpressionPartOperatorCompare = class(TFpPascalExpressionPartBinaryOperator) // = < > <> ><
protected
procedure Init; override;
function DoGetResultValue: TFpDbgValue; override;
end;
{ TFpPascalExpressionPartOperatorMemberOf }
TFpPascalExpressionPartOperatorMemberOf = class(TFpPascalExpressionPartBinaryOperator) // struct.member
protected
procedure Init; override;
function IsValidNextPart(APart: TFpPascalExpressionPart): Boolean; override;
function DoGetResultValue: TFpDbgValue; override;
end;
implementation
const
// 1 highest
PRECEDENCE_MEMBER_OF = 1; // foo.bar
PRECEDENCE_MAKE_REF = 1; // ^TFoo
PRECEDENCE_ARG_LIST = 2; // foo() / TFoo()
PRECEDENCE_ARRAY_IDX = 2; // foo[1]
PRECEDENCE_DEREF = 5; // a^ // Precedence acts only to the left side
PRECEDENCE_ADRESS_OF = 6; // @a
//PRECEDENCE_POWER = 10; // ** (power) must be stronger than unary -
PRECEDENCE_UNARY_SIGN = 11; // -a
PRECEDENCE_MUL_DIV = 12; // a * b
PRECEDENCE_PLUS_MINUS = 13; // a + b
PRECEDENCE_COMPARE = 20; // a <> b // a=b
type
{%region DebugSymbol }
{ TPasParserSymbolPointer
used by TFpPasParserValueMakeReftype.GetDbgSymbol
}
TPasParserSymbolPointer = class(TFpDbgSymbol)
private
FPointerLevels: Integer;
FPointedTo: TFpDbgSymbol;
FContext: TFpDbgInfoContext;
protected
// NameNeeded // "^TPointedTo"
procedure TypeInfoNeeded; override;
public
constructor Create(const APointedTo: TFpDbgSymbol; AContext: TFpDbgInfoContext; APointerLevels: Integer);
constructor Create(const APointedTo: TFpDbgSymbol; AContext: TFpDbgInfoContext);
destructor Destroy; override;
function TypeCastValue(AValue: TFpDbgValue): TFpDbgValue; override;
end;
{ TPasParserSymbolArrayDeIndex }
TPasParserSymbolArrayDeIndex = class(TDbgSymbolForwarder) // 1 index level off
private
FArray: TFpDbgSymbol;
protected
//procedure ForwardToSymbolNeeded; override;
function GetMemberCount: Integer; override;
function GetMember(AIndex: Int64): TFpDbgSymbol; override;
public
constructor Create(const AnArray: TFpDbgSymbol);
destructor Destroy; override;
end;
{%endregion DebugSymbol }
{%region DebugSymbolValue }
{ TFpPasParserValue }
TFpPasParserValue = class(TFpDbgValue)
private
FContext: TFpDbgInfoContext;
protected
function DebugText(AIndent: String): String; virtual;
public
constructor Create(AContext: TFpDbgInfoContext);
property Context: TFpDbgInfoContext read FContext;
end;
{ TFpPasParserValueCastToPointer
used by TPasParserSymbolPointer.TypeCastValue (which is used by TFpPasParserValueMakeReftype.GetDbgSymbol)
}
TFpPasParserValueCastToPointer = class(TFpPasParserValue)
private
FValue: TFpDbgValue;
FTypeSymbol: TFpDbgSymbol;
FLastMember: TFpDbgValue;
protected
function DebugText(AIndent: String): String; override;
protected
function GetKind: TDbgSymbolKind; override;
function GetFieldFlags: TFpDbgValueFieldFlags; override;
function GetTypeInfo: TFpDbgSymbol; override;
function GetAsCardinal: QWord; override;
function GetDataAddress: TFpDbgMemLocation; override;
function GetMember(AIndex: Int64): TFpDbgValue; override;
public
constructor Create(AValue: TFpDbgValue; ATypeInfo: TFpDbgSymbol; AContext: TFpDbgInfoContext);
destructor Destroy; override;
end;
{ TFpPasParserValueMakeReftype }
TFpPasParserValueMakeReftype = class(TFpPasParserValue)
private
FSourceTypeSymbol, FTypeSymbol: TFpDbgSymbol;
FRefLevel: Integer;
protected
function DebugText(AIndent: String): String; override;
protected
function GetDbgSymbol: TFpDbgSymbol; override; // returns a TPasParserSymbolPointer
public
constructor Create(ATypeInfo: TFpDbgSymbol; AContext: TFpDbgInfoContext);
destructor Destroy; override;
procedure IncRefLevel;
function GetTypeCastedValue(ADataVal: TFpDbgValue): TFpDbgValue; override;
end;
{ TFpPasParserValueDerefPointer
Used as address source in typecast
}
TFpPasParserValueDerefPointer = class(TFpPasParserValue)
private
FValue: TFpDbgValue;
FAddressOffset: Int64; // Add to address
FCardinal: QWord; // todo: TFpDbgMemLocation ?
FCardinalRead: Boolean;
protected
function DebugText(AIndent: String): String; override;
protected
function GetFieldFlags: TFpDbgValueFieldFlags; override;
function GetAddress: TFpDbgMemLocation; override;
function GetSize: Integer; override;
function GetAsCardinal: QWord; override; // reads men
function GetTypeInfo: TFpDbgSymbol; override; // TODO: Cardinal? Why? // TODO: does not handle AOffset
public
constructor Create(AValue: TFpDbgValue; AContext: TFpDbgInfoContext);
constructor Create(AValue: TFpDbgValue; AContext: TFpDbgInfoContext; AOffset: Int64);
destructor Destroy; override;
end;
{ TFpPasParserValueAddressOf }
TFpPasParserValueAddressOf = class(TFpPasParserValue)
private
FValue: TFpDbgValue;
FTypeInfo: TFpDbgSymbol;
FLastMember: TFpDbgValue;
function GetPointedToValue: TFpDbgValue;
protected
function DebugText(AIndent: String): String; override;
protected
function GetKind: TDbgSymbolKind; override;
function GetFieldFlags: TFpDbgValueFieldFlags; override;
function GetAsInteger: Int64; override;
function GetAsCardinal: QWord; override;
function GetTypeInfo: TFpDbgSymbol; override;
function GetDataAddress: TFpDbgMemLocation; override;
function GetMember(AIndex: Int64): TFpDbgValue; override;
public
constructor Create(AValue: TFpDbgValue; AContext: TFpDbgInfoContext);
destructor Destroy; override;
property PointedToValue: TFpDbgValue read GetPointedToValue;
end;
{%endregion DebugSymbolValue }
function DbgsResultValue(AVal: TFpDbgValue; AIndent: String): String;
begin
if (AVal <> nil) and (AVal is TFpPasParserValue) then
Result := LineEnding + TFpPasParserValue(AVal).DebugText(AIndent)
else
if AVal <> nil then
Result := DbgSName(AVal) + ' DbsSym='+DbgSName(AVal.DbgSymbol)+' Type='+DbgSName(AVal.TypeInfo)
else
Result := DbgSName(AVal);
end;
function DbgsSymbol(AVal: TFpDbgSymbol; {%H-}AIndent: String): String;
begin
Result := DbgSName(AVal);
end;
function TFpPasParserValue.DebugText(AIndent: String): String;
begin
Result := AIndent + DbgSName(Self) + ' DbsSym='+DbgSName(DbgSymbol)+' Type='+DbgSName(TypeInfo) + LineEnding;
end;
constructor TFpPasParserValue.Create(AContext: TFpDbgInfoContext);
begin
FContext := AContext;
inherited Create;
end;
{ TPasParserSymbolValueCastToPointer }
function TFpPasParserValueCastToPointer.DebugText(AIndent: String): String;
begin
Result := inherited DebugText(AIndent)
+ AIndent + '-Value= ' + DbgsResultValue(FValue, AIndent + ' ') + LineEnding
+ AIndent + '-Symbol = ' + DbgsSymbol(FTypeSymbol, AIndent + ' ') + LineEnding;
end;
function TFpPasParserValueCastToPointer.GetKind: TDbgSymbolKind;
begin
Result := skPointer;
end;
function TFpPasParserValueCastToPointer.GetFieldFlags: TFpDbgValueFieldFlags;
begin
if (FValue.FieldFlags * [svfAddress, svfCardinal] <> [])
then
Result := [svfOrdinal, svfCardinal, svfSizeOfPointer, svfDataAddress]
else
Result := [];
end;
function TFpPasParserValueCastToPointer.GetTypeInfo: TFpDbgSymbol;
begin
Result := FTypeSymbol;
end;
function TFpPasParserValueCastToPointer.GetAsCardinal: QWord;
var
f: TFpDbgValueFieldFlags;
begin
Result := 0;
f := FValue.FieldFlags;
if svfCardinal in f then
Result := FValue.AsCardinal
else
if svfAddress in f then begin
if not FContext.MemManager.ReadUnsignedInt(FValue.Address, FContext.SizeOfAddress, Result) then
Result := 0;
end
else
Result := 0;
end;
function TFpPasParserValueCastToPointer.GetDataAddress: TFpDbgMemLocation;
begin
Result := TargetLoc(TDbgPtr(AsCardinal));
end;
function TFpPasParserValueCastToPointer.GetMember(AIndex: Int64): TFpDbgValue;
var
ti: TFpDbgSymbol;
addr: TFpDbgMemLocation;
Tmp: TFpDbgValueConstAddress;
begin
Result := nil;
ti := FTypeSymbol.TypeInfo;
addr := DataAddress;
if not IsTargetAddr(addr) then begin
//LastError := CreateError(fpErrAnyError, ['Internal dereference error']);
exit;
end;
{$PUSH}{$R-}{$Q-} // TODO: check overflow
if ti <> nil then
AIndex := AIndex * ti.Size;
addr.Address := addr.Address + AIndex;
{$POP}
Tmp := TFpDbgValueConstAddress.Create(addr);
if ti <> nil then begin
Result := ti.TypeCastValue(Tmp);
Tmp.ReleaseReference;
end
else
Result := Tmp;
FLastMember := Result;
end;
constructor TFpPasParserValueCastToPointer.Create(AValue: TFpDbgValue;
ATypeInfo: TFpDbgSymbol; AContext: TFpDbgInfoContext);
begin
inherited Create(AContext);
FValue := AValue;
FValue.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FValue, 'TPasParserSymbolValueCastToPointer'){$ENDIF};
FTypeSymbol := ATypeInfo;
FTypeSymbol.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FTypeSymbol, 'TPasParserSymbolValueCastToPointer'){$ENDIF};
Assert((FTypeSymbol=nil) or (FTypeSymbol.Kind = skPointer), 'TPasParserSymbolValueCastToPointer.Create');
end;
destructor TFpPasParserValueCastToPointer.Destroy;
begin
FLastMember.ReleaseReference;
FValue.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FValue, 'TPasParserSymbolValueCastToPointer'){$ENDIF};
FTypeSymbol.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FTypeSymbol, 'TPasParserSymbolValueCastToPointer'){$ENDIF};
inherited Destroy;
end;
{ TPasParserSymbolValueMakeReftype }
function TFpPasParserValueMakeReftype.DebugText(AIndent: String): String;
begin
Result := inherited DebugText(AIndent)
+ AIndent + '-RefLevel = ' + dbgs(FRefLevel) + LineEnding
+ AIndent + '-SourceSymbol = ' + DbgsSymbol(FSourceTypeSymbol, AIndent + ' ') + LineEnding
+ AIndent + '-Symbol = ' + DbgsSymbol(FTypeSymbol, AIndent + ' ') + LineEnding;
end;
function TFpPasParserValueMakeReftype.GetDbgSymbol: TFpDbgSymbol;
begin
if FTypeSymbol = nil then begin
FTypeSymbol := TPasParserSymbolPointer.Create(FSourceTypeSymbol, FContext, FRefLevel);
{$IFDEF WITH_REFCOUNT_DEBUG}FTypeSymbol.DbgRenameReference(@FSourceTypeSymbol, 'TPasParserSymbolValueMakeReftype'){$ENDIF};
end;
Result := FTypeSymbol;
end;
constructor TFpPasParserValueMakeReftype.Create(ATypeInfo: TFpDbgSymbol;
AContext: TFpDbgInfoContext);
begin
inherited Create(AContext);
FSourceTypeSymbol := ATypeInfo;
FSourceTypeSymbol.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FSourceTypeSymbol, 'TPasParserSymbolValueMakeReftype'){$ENDIF};
FRefLevel := 1;
end;
destructor TFpPasParserValueMakeReftype.Destroy;
begin
FSourceTypeSymbol.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FSourceTypeSymbol, 'TPasParserSymbolValueMakeReftype'){$ENDIF};
FTypeSymbol.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FSourceTypeSymbol, 'TPasParserSymbolValueMakeReftype'){$ENDIF};
inherited Destroy;
end;
procedure TFpPasParserValueMakeReftype.IncRefLevel;
begin
inc(FRefLevel);
end;
function TFpPasParserValueMakeReftype.GetTypeCastedValue(ADataVal: TFpDbgValue): TFpDbgValue;
begin
Result := DbgSymbol.TypeCastValue(ADataVal);
end;
{ TPasParserDerefPointerSymbolValue }
function TFpPasParserValueDerefPointer.DebugText(AIndent: String): String;
begin
Result := inherited DebugText(AIndent)
+ AIndent + '-Value= ' + DbgsResultValue(FValue, AIndent + ' ') + LineEnding;
end;
function TFpPasParserValueDerefPointer.GetFieldFlags: TFpDbgValueFieldFlags;
var
t: TFpDbgSymbol;
begin
// MUST *NOT* have ordinal
Result := [svfAddress];
t := FValue.TypeInfo;
if t <> nil then t := t.TypeInfo;
if t <> nil then
if t.Kind = skPointer then begin
//Result := Result + [svfSizeOfPointer];
Result := Result + [svfSizeOfPointer, svfCardinal, svfOrdinal]; // TODO: svfCardinal ???
end
else
Result := Result + [svfSize];
end;
function TFpPasParserValueDerefPointer.GetAddress: TFpDbgMemLocation;
begin
Result := FValue.DataAddress;
if FAddressOffset <> 0 then begin
assert(IsTargetAddr(Result ), 'TFpPasParserValueDerefPointer.GetAddress: TargetLoc(Result)');
if IsTargetAddr(Result) then
Result.Address := Result.Address + FAddressOffset
else
Result := InvalidLoc;
end;
end;
function TFpPasParserValueDerefPointer.GetSize: Integer;
var
t: TFpDbgSymbol;
begin
t := FValue.TypeInfo;
if t <> nil then t := t.TypeInfo;
if t <> nil then
Result := t.Size
else
Result := inherited GetSize;
end;
function TFpPasParserValueDerefPointer.GetAsCardinal: QWord;
var
m: TFpDbgMemManager;
Addr: TFpDbgMemLocation;
Ctx: TFpDbgInfoContext;
AddrSize: Integer;
begin
Result := FCardinal;
if FCardinalRead then exit;
Ctx := Context;
if Ctx = nil then exit;
AddrSize := Ctx.SizeOfAddress;
if (AddrSize <= 0) or (AddrSize > SizeOf(FCardinal)) then exit;
m := Ctx.MemManager;
if m = nil then exit;
FCardinal := 0;
FCardinalRead := True;
Addr := GetAddress;
if not IsReadableLoc(Addr) then exit;
FCardinal := LocToAddrOrNil(m.ReadAddress(Addr, Ctx.SizeOfAddress));
Result := FCardinal;
end;
function TFpPasParserValueDerefPointer.GetTypeInfo: TFpDbgSymbol;
var
t: TFpDbgSymbol;
begin
t := FValue.TypeInfo;
if t <> nil then t := t.TypeInfo;
if t <> nil then
Result := t
else
Result := inherited GetTypeInfo;
end;
constructor TFpPasParserValueDerefPointer.Create(AValue: TFpDbgValue;
AContext: TFpDbgInfoContext);
begin
Create(AValue, AContext, 0);
end;
constructor TFpPasParserValueDerefPointer.Create(AValue: TFpDbgValue;
AContext: TFpDbgInfoContext; AOffset: Int64);
begin
inherited Create(AContext);
FValue := AValue;
FValue.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FValue, 'TPasParserDerefPointerSymbolValue'){$ENDIF};
FAddressOffset := AOffset;
end;
destructor TFpPasParserValueDerefPointer.Destroy;
begin
inherited Destroy;
FValue.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FValue, 'TPasParserDerefPointerSymbolValue'){$ENDIF};
end;
{ TPasParserAddressOfSymbolValue }
function TFpPasParserValueAddressOf.GetPointedToValue: TFpDbgValue;
begin
Result := FValue;
end;
function TFpPasParserValueAddressOf.DebugText(AIndent: String): String;
begin
Result := inherited DebugText(AIndent)
+ AIndent + '-Value= ' + DbgsResultValue(FValue, AIndent + ' ') + LineEnding
+ AIndent + '-Symbol = ' + DbgsSymbol(FTypeInfo, AIndent + ' ') + LineEnding;
end;
function TFpPasParserValueAddressOf.GetKind: TDbgSymbolKind;
begin
Result := skPointer;
end;
function TFpPasParserValueAddressOf.GetFieldFlags: TFpDbgValueFieldFlags;
begin
Result := [svfOrdinal, svfCardinal, svfSizeOfPointer, svfDataAddress];
end;
function TFpPasParserValueAddressOf.GetAsInteger: Int64;
begin
Result := Int64(LocToAddrOrNil(FValue.Address));
end;
function TFpPasParserValueAddressOf.GetAsCardinal: QWord;
begin
Result := QWord(LocToAddrOrNil(FValue.Address));
end;
function TFpPasParserValueAddressOf.GetTypeInfo: TFpDbgSymbol;
begin
Result := FTypeInfo;
if Result <> nil then
exit;
if FValue.TypeInfo = nil then
exit;
FTypeInfo := TPasParserSymbolPointer.Create(FValue.TypeInfo, FContext);
{$IFDEF WITH_REFCOUNT_DEBUG}FTypeInfo.DbgRenameReference(@FTypeInfo, 'TPasParserAddressOfSymbolValue');{$ENDIF}
Result := FTypeInfo;
end;
function TFpPasParserValueAddressOf.GetDataAddress: TFpDbgMemLocation;
begin
Result := FValue.Address;
end;
function TFpPasParserValueAddressOf.GetMember(AIndex: Int64): TFpDbgValue;
var
ti: TFpDbgSymbol;
addr: TFpDbgMemLocation;
Tmp: TFpDbgValueConstAddress;
begin
if (AIndex = 0) or (FValue = nil) then begin
Result := FValue;
exit;
end;
Result := nil;
ti := FValue.TypeInfo;
addr := FValue.Address;
if not IsTargetAddr(addr) then begin
//LastError := CreateError(fpErrAnyError, ['Internal dereference error']);
exit;
end;
{$PUSH}{$R-}{$Q-} // TODO: check overflow
if ti <> nil then
AIndex := AIndex * ti.Size;
addr.Address := addr.Address + AIndex;
{$POP}
Tmp := TFpDbgValueConstAddress.Create(addr);
if ti <> nil then begin
Result := ti.TypeCastValue(Tmp);
Tmp.ReleaseReference;
end
else
Result := Tmp;
FLastMember := Result;
end;
constructor TFpPasParserValueAddressOf.Create(AValue: TFpDbgValue;
AContext: TFpDbgInfoContext);
begin
inherited Create(AContext);
FValue := AValue;
FValue.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FValue, 'TPasParserAddressOfSymbolValue'){$ENDIF};
end;
destructor TFpPasParserValueAddressOf.Destroy;
begin
inherited Destroy;
FLastMember.ReleaseReference;
FValue.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FValue, 'TPasParserAddressOfSymbolValue'){$ENDIF};
FTypeInfo.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FTypeInfo, 'TPasParserAddressOfSymbolValue'){$ENDIF};
end;
{ TPasParserSymbolArrayDeIndex }
function TPasParserSymbolArrayDeIndex.GetMemberCount: Integer;
begin
Result := (inherited GetMemberCount) - 1;
end;
function TPasParserSymbolArrayDeIndex.GetMember(AIndex: Int64): TFpDbgSymbol;
begin
Result := inherited GetMember(AIndex + 1);
end;
constructor TPasParserSymbolArrayDeIndex.Create(const AnArray: TFpDbgSymbol);
begin
FArray := AnArray;
FArray.AddReference;
inherited Create('');
SetKind(skArray);
SetForwardToSymbol(FArray);
end;
destructor TPasParserSymbolArrayDeIndex.Destroy;
begin
ReleaseRefAndNil(FArray);
inherited Destroy;
end;
{ TPasParserSymbolPointer }
procedure TPasParserSymbolPointer.TypeInfoNeeded;
var
t: TPasParserSymbolPointer;
begin
assert(FPointerLevels > 1, 'TPasParserSymbolPointer.TypeInfoNeeded: FPointerLevels > 1');
t := TPasParserSymbolPointer.Create(FPointedTo, FContext, FPointerLevels-1);
SetTypeInfo(t);
t.ReleaseReference;
end;
constructor TPasParserSymbolPointer.Create(const APointedTo: TFpDbgSymbol;
AContext: TFpDbgInfoContext; APointerLevels: Integer);
begin
inherited Create('');
FContext := AContext;
FPointerLevels := APointerLevels;
FPointedTo := APointedTo;
FPointedTo.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(FPointedTo, 'TPasParserSymbolPointer'){$ENDIF};
if APointerLevels = 1 then
SetTypeInfo(APointedTo);
SetKind(skPointer);
SetSymbolType(stType);
end;
constructor TPasParserSymbolPointer.Create(const APointedTo: TFpDbgSymbol;
AContext: TFpDbgInfoContext);
begin
Create(APointedTo, AContext, 1);
end;
destructor TPasParserSymbolPointer.Destroy;
begin
FPointedTo.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(FPointedTo, 'TPasParserSymbolPointer'){$ENDIF};
inherited Destroy;
end;
function TPasParserSymbolPointer.TypeCastValue(AValue: TFpDbgValue): TFpDbgValue;
begin
Result := TFpPasParserValueCastToPointer.Create(AValue, Self, FContext);
end;
{ TFpPascalExpressionPartBracketIndex }
procedure TFpPascalExpressionPartBracketIndex.Init;
begin
FPrecedence := PRECEDENCE_ARRAY_IDX;
inherited Init;
end;
function TFpPascalExpressionPartBracketIndex.DoGetResultValue: TFpDbgValue;
var
TmpVal, TmpVal2, TmpIndex: TFpDbgValue;
i: Integer;
Offs: Int64;
ti: TFpDbgSymbol;
IsPChar: Boolean;
begin
Result := nil;
assert(Count >= 2, 'TFpPascalExpressionPartBracketIndex.DoGetResultValue: Count >= 2');
if Count < 2 then exit;
TmpVal := Items[0].ResultValue;
if TmpVal = nil then exit;
TmpVal.AddReference;
for i := 1 to Count - 1 do begin
TmpVal2 := nil;
TmpIndex := Items[i].ResultValue;
if TmpIndex = nil then begin
// error should be set by Items[i]
TmpVal.ReleaseReference;
exit;
end;
case TmpVal.Kind of
skArray: begin
if (svfInteger in TmpIndex.FieldFlags) then
TmpVal2 := TmpVal.Member[TmpIndex.AsInteger]
else
if (svfOrdinal in TmpIndex.FieldFlags) and
(TmpIndex.AsCardinal <= high(Int64))
then
TmpVal2 := TmpVal.Member[TmpIndex.AsCardinal]
else
begin
SetError('Can not calculate Index');
TmpVal.ReleaseReference;
exit;
end;
if TmpVal2 <> nil then TmpVal2.AddReference;
end; // Kind = skArray
skPointer: begin
if (svfInteger in TmpIndex.FieldFlags) then
Offs := TmpIndex.AsInteger
else
if (svfOrdinal in TmpIndex.FieldFlags) and (TmpIndex.AsCardinal <= high(Int64))
then
Offs := Int64(TmpIndex.AsCardinal)
else
begin
SetError('Can not calculate Index');
TmpVal.ReleaseReference;
exit;
end;
ti := TmpVal.TypeInfo;
if (ti <> nil) then ti := ti.TypeInfo;
IsPChar := (ti <> nil) and (ti.Kind in [skChar]) and (Offs > 0);
if IsPChar then FExpression.FHasPCharIndexAccess := True;
if IsPChar and FExpression.FixPCharIndexAccess then begin
// fix for string in dwarf 2
if Offs > 0 then
dec(Offs);
end;
TmpVal2 := TmpVal.Member[Offs];
if IsError(TmpVal.LastError) then
SetError('Error dereferencing'); // TODO: set correct error
if TmpVal2 <> nil then TmpVal2.AddReference;
end;
skString: begin
//TODO
SetError('Not implemented');
TmpVal.ReleaseReference;
exit;
end
else
begin
SetError(fpErrTypeHasNoIndex, [GetText]);
TmpVal.ReleaseReference;
exit;
end;
end;
if TmpVal2 = nil then begin
SetError('Internal Error, attempting to read array element');
TmpVal.ReleaseReference;
exit;
end;
TmpVal.ReleaseReference;
TmpVal := TmpVal2;
end;
Result := TmpVal;
{$IFDEF WITH_REFCOUNT_DEBUG}if Result <> nil then Result.DbgRenameReference(nil, 'DoGetResultValue'){$ENDIF};
end;
function TFpPascalExpressionPartBracketIndex.IsValidAfterPart(APrevPart: TFpPascalExpressionPart): Boolean;
begin
Result := inherited IsValidAfterPart(APrevPart);
Result := Result and APrevPart.CanHaveOperatorAsNext;
if (APrevPart.Parent <> nil) and (APrevPart.Parent.HasPrecedence) then
Result := False;
end;
function TFpPascalExpressionPartBracketIndex.HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart;
begin
Result := Self;
if Count < 1 then begin // Todo a,b,c
SetError(APart, 'Internal error handling [] '+GetText+': '); // Missing the array on which this index works
APart.Free;
exit;
end;
if (Count > 1) and (not AfterComma) then begin
SetError(APart, 'Comma or closing "]" expected '+GetText+': ');
APart.Free;
exit;
end;
if not IsValidNextPart(APart) then begin
SetError(APart, 'Invalid operand in [] '+GetText+': ');
APart.Free;
exit;
end;
Add(APart);
Result := APart;
end;
function TFpPascalExpressionPartBracketIndex.MaybeHandlePrevPart(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean;
var
ALeftSide: TFpPascalExpressionPart;
begin
//Result := MaybeAddLeftOperand(APrevPart, AResult);
Result := APrevPart.IsValidNextPart(Self);
if not Result then
exit;
AResult := Self;
if (Count > 0) // function/type already set
then begin
SetError(APrevPart, 'Parse error in () '+GetText+': ');
APrevPart.Free;
exit;
end;
ALeftSide := APrevPart.FindLeftSideOperandByPrecedence(Self);
if ALeftSide = nil then begin
SetError(Self, 'Internal parser error for operator '+GetText+': ');
APrevPart.Free;
exit;
end;
ALeftSide.ReplaceInParent(Self);
Add(ALeftSide);
end;
procedure TFpPascalExpressionPartBracketIndex.DoHandleEndOfExpression;
begin
inherited DoHandleEndOfExpression;
if (Count < 2) then
SetError(fpErrPasParserMissingIndexExpression, [GetText]);
end;
function TFpPascalExpressionPartBracketIndex.HandleSeparator(ASeparatorType: TSeparatorType): Boolean;
begin
if (not (ASeparatorType = ppstComma)) or IsClosed then begin
Result := inherited HandleSeparator(ASeparatorType);
exit;
end;
Result := (Count > FAfterComma) and (Count > 1); // First element is name of array (in front of "[")
if Result then
SetAfterCommaFlag;
end;
{ TFpPascalExpressionPartBracketSet }
function TFpPascalExpressionPartBracketSet.HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart;
begin
Result := Self;
if (Count > 0) and (not AfterComma) then begin
SetError('To many expressions'); // TODO comma
APart.Free;
exit;
end;
Result := APart;
Add(APart);
end;
function TFpPascalExpressionPartBracketSet.HandleSeparator(ASeparatorType: TSeparatorType): Boolean;
begin
if (not (ASeparatorType = ppstComma)) or IsClosed then begin
Result := inherited HandleSeparator(ASeparatorType);
exit;
end;
Result := (Count > FAfterComma) and (Count > 0);
if Result then
SetAfterCommaFlag;
end;
{ TFpPascalExpressionPartWithPrecedence }
function TFpPascalExpressionPartWithPrecedence.HasPrecedence: Boolean;
begin
Result := True;
end;
{ TFpPascalExpressionPartBracketArgumentList }
procedure TFpPascalExpressionPartBracketArgumentList.Init;
begin
FPrecedence := PRECEDENCE_ARG_LIST;
inherited Init;
end;
function TFpPascalExpressionPartBracketArgumentList.DoGetResultValue: TFpDbgValue;
var
tmp, tmp2: TFpDbgValue;
begin
Result := nil;
if (Count = 0) then begin
SetError(fpErrPasParserInvalidExpression, []);
exit;
end;
tmp := Items[0].ResultValue;
if (tmp = nil) or (not Expression.Valid) then
exit;
if (Count = 2) then begin
//TODO if tmp is TFpPascalExpressionPartOperatorMakeRef then
// AVOID creating the TPasParserSymbolPointer by calling tmp.DbgSymbol
// it ran be created in TPasParserSymbolValueCastToPointer if needed.
if (tmp <> nil) and (tmp.DbgSymbol <> nil) and
(tmp.DbgSymbol.SymbolType = stType)
then begin
// This is a typecast
tmp2 := Items[1].ResultValue;
if tmp2 <> nil then
Result := tmp.GetTypeCastedValue(tmp2);
//Result := tmp.DbgSymbol.TypeCastValue(tmp2);
if Result <> nil then
{$IFDEF WITH_REFCOUNT_DEBUG}Result.DbgRenameReference(nil, 'DoGetResultValue'){$ENDIF};
exit;
end;
end;
// Must be function call
SetError('No support for calling functions');
end;
function TFpPascalExpressionPartBracketArgumentList.DoGetIsTypeCast: Boolean;
begin
Result := False;
end;
function TFpPascalExpressionPartBracketArgumentList.IsValidAfterPart(APrevPart: TFpPascalExpressionPart): Boolean;
begin
Result := inherited IsValidAfterPart(APrevPart);
Result := Result and APrevPart.CanHaveOperatorAsNext;
if (APrevPart.Parent <> nil) and (APrevPart.Parent.HasPrecedence) then
Result := False;
end;
function TFpPascalExpressionPartBracketArgumentList.HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart;
begin
Result := Self;
if Count < 1 then begin // Todo a,b,c
SetError(APart, 'Internal error handling () '+GetText+': '); // Missing the functionname on which this index works
APart.Free;
exit;
end;
if (Count > 1) and (not AfterComma) then begin // Todo a,b,c
SetError(APart, 'Comma or closing ")" expected: '+GetText+': ');
APart.Free;
exit;
end;
if not IsValidNextPart(APart) then begin
SetError(APart, 'Invalid operand in () '+GetText+': ');
APart.Free;
exit;
end;
Add(APart);
Result := APart;
end;
function TFpPascalExpressionPartBracketArgumentList.MaybeHandlePrevPart(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean;
var
ALeftSide: TFpPascalExpressionPart;
begin
//Result := MaybeAddLeftOperand(APrevPart, AResult);
Result := APrevPart.IsValidNextPart(Self);
if not Result then
exit;
AResult := Self;
if (Count > 0) // function/type already set
then begin
SetError(APrevPart, 'Parse error in () '+GetText+': ');
APrevPart.Free;
exit;
end;
ALeftSide := APrevPart.FindLeftSideOperandByPrecedence(Self);
if ALeftSide = nil then begin
SetError(Self, 'Internal parser error for operator '+GetText+': ');
APrevPart.Free;
exit;
end;
ALeftSide.ReplaceInParent(Self);
Add(ALeftSide);
end;
function TFpPascalExpressionPartBracketArgumentList.HandleSeparator(ASeparatorType: TSeparatorType): Boolean;
begin
if (not (ASeparatorType = ppstComma)) or IsClosed then begin
Result := inherited HandleSeparator(ASeparatorType);
exit;
end;
Result := (Count > FAfterComma) and (Count > 1); // First element is name of function (in front of "(")
if Result then
SetAfterCommaFlag;
end;
{ TFpPascalExpressionPartBracketSubExpression }
function TFpPascalExpressionPartBracketSubExpression.HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart;
begin
Result := Self;
if Count > 0 then begin
SetError('To many expressions');
APart.Free;
exit;
end;
Result := APart;
Add(APart);
end;
function TFpPascalExpressionPartBracketSubExpression.DoGetResultValue: TFpDbgValue;
begin
if Count <> 1 then
Result := nil
else
Result := Items[0].ResultValue;
if Result <> nil then
Result.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(nil, 'DoGetResultValue'){$ENDIF};
end;
{ TFpPascalExpressionPartIdentifier }
function TFpPascalExpressionPartIdentifier.DoGetIsTypeCast: Boolean;
begin
Result := (ResultValue <> nil) and (ResultValue.DbgSymbol <> nil) and (ResultValue.DbgSymbol.SymbolType = stType);
end;
function TFpPascalExpressionPartIdentifier.DoGetResultValue: TFpDbgValue;
var
s: String;
tmp: TFpDbgValueConstAddress;
begin
s := GetText;
Result := FExpression.GetDbgSymbolForIdentifier(s);
if Result = nil then begin
s := LowerCase(s);
if s = 'nil' then begin
tmp := TFpDbgValueConstAddress.Create(NilLoc);
Result := TFpPasParserValueAddressOf.Create(tmp, Expression.Context);
tmp.ReleaseReference;
{$IFDEF WITH_REFCOUNT_DEBUG}Result.DbgRenameReference(nil, 'DoGetResultValue');{$ENDIF}
end
else
if s = 'true' then begin
Result := TFpDbgValueConstBool.Create(True);
{$IFDEF WITH_REFCOUNT_DEBUG}Result.DbgRenameReference(nil, 'DoGetResultValue');{$ENDIF}
end
else
if s = 'false' then begin
Result := TFpDbgValueConstBool.Create(False);
{$IFDEF WITH_REFCOUNT_DEBUG}Result.DbgRenameReference(nil, 'DoGetResultValue');{$ENDIF}
end
else begin
SetError(fpErrSymbolNotFound, [GetText]);
exit;
end;
end
else
Result.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(nil, 'DoGetResultValue'){$ENDIF};
end;
function GetFirstToken(AText: PChar): String;
begin
Result := AText[0];
if AText^ in ['a'..'z', 'A'..'Z', '_', '0'..'9'] then begin
inc(AText);
while (AText^ in ['a'..'z', 'A'..'Z', '_', '0'..'9']) and (Length(Result) < 200) do begin
Result := Result + AText[0];
inc(AText);
end;
end
else
begin
inc(AText);
while not (AText^ in [#0..#32, 'a'..'z', 'A'..'Z', '_', '0'..'9']) and (Length(Result) < 100) do begin
Result := Result + AText[0];
inc(AText);
end;
end;
end;
{ TFpPascalExpressionPartConstantNumber }
function TFpPascalExpressionPartConstantNumber.DoGetResultValue: TFpDbgValue;
var
i: QWord;
e: word;
begin
Val(GetText, i, e);
if e <> 0 then begin
Result := nil;
SetError(fpErrInvalidNumber, [GetText]);
exit;
end;
if FStartChar^ in ['0'..'9'] then
Result := TFpDbgValueConstNumber.Create(i, False)
else
Result := TFpDbgValueConstNumber.Create(Int64(i), True); // hex,oct,bin values default to signed
{$IFDEF WITH_REFCOUNT_DEBUG}Result.DbgRenameReference(nil, 'DoGetResultValue'){$ENDIF};
end;
{ TFpPascalExpressionPartConstantNumberFloat }
function TFpPascalExpressionPartConstantNumberFloat.DoGetResultValue: TFpDbgValue;
var
f: Extended;
s: String;
begin
s := GetText;
if not TextToFloat(PChar(s), f) then begin
Result := nil;
SetError(fpErrInvalidNumber, [GetText]);
exit;
end;
Result := TFpDbgValueConstFloat.Create(f);
{$IFDEF WITH_REFCOUNT_DEBUG}Result.DbgRenameReference(nil, 'DoGetResultValue'){$ENDIF};
end;
{ TFpPascalExpression }
procedure TFpPascalExpression.Parse;
var
CurPtr, EndPtr, TokenEndPtr: PChar;
CurPart, NewPart: TFpPascalExpressionPart;
procedure AddPart(AClass: TFpPascalExpressionPartClass);
begin
NewPart := AClass.Create(Self, CurPtr, TokenEndPtr-1);
end;
procedure AddPlusMinus;
begin
if (CurPart = nil) or (not CurPart.CanHaveOperatorAsNext)
then AddPart(TFpPascalExpressionPartOperatorUnaryPlusMinus)
else AddPart(TFpPascalExpressionPartOperatorPlusMinus);
end;
procedure AddIdentifier;
begin
while TokenEndPtr^ in ['a'..'z', 'A'..'Z', '_', '0'..'9'] do
inc(TokenEndPtr);
// TODO: Check functions not, and, in, as, is ...
case TokenEndPtr - CurPtr of
3: case CurPtr^ of
'd', 'D':
if (CurPtr[1] in ['i', 'I']) and (CurPtr[2] in ['v', 'V']) then
NewPart := TFpPascalExpressionPartOperatorMulDiv.Create(Self, CurPtr, TokenEndPtr-1);
end;
end;
if NewPart = nil then
NewPart := TFpPascalExpressionPartIdentifier.Create(Self, CurPtr, TokenEndPtr-1);
end;
procedure HandleDot;
begin
while TokenEndPtr^ = '.' do
inc(TokenEndPtr);
case TokenEndPtr - CurPtr of
1: AddPart(TFpPascalExpressionPartOperatorMemberOf);
//2: ; // ".."
else SetError('Failed parsing ...');
end;
end;
procedure AddRefOperator;
begin
if (CurPart = nil) or (not CurPart.CanHaveOperatorAsNext)
then AddPart(TFpPascalExpressionPartOperatorMakeRef)
else AddPart(TFpPascalExpressionPartOperatorDeRef);
end;
procedure HandleRoundBracket;
begin
if (CurPart = nil) or (not CurPart.CanHaveOperatorAsNext)
then AddPart(TFpPascalExpressionPartBracketSubExpression)
else AddPart(TFpPascalExpressionPartBracketArgumentList);
end;
procedure HandleSqareBracket;
begin
if (CurPart = nil) or (not CurPart.CanHaveOperatorAsNext)
then AddPart(TFpPascalExpressionPartBracketSet)
else AddPart(TFpPascalExpressionPartBracketIndex);
end;
procedure CloseBracket(ABracketClass: TFpPascalExpressionPartBracketClass);
var
BracketPart: TFpPascalExpressionPartBracket;
begin
BracketPart := CurPart.SurroundingBracket;
if BracketPart = nil then begin
SetError('Closing bracket found without opening')
end
else
if not (BracketPart is ABracketClass) then begin
SetError('Mismatch bracket')
end
else begin
TFpPascalExpressionPartBracket(BracketPart).CloseBracket;
CurPart := BracketPart;
end;
end;
procedure AddConstNumber;
begin
case CurPtr^ of
'$': while TokenEndPtr^ in ['a'..'z', 'A'..'Z', '0'..'9'] do inc(TokenEndPtr);
'&': while TokenEndPtr^ in ['0'..'7'] do inc(TokenEndPtr);
'%': while TokenEndPtr^ in ['0'..'1'] do inc(TokenEndPtr);
'0'..'9':
if (CurPtr^ = '0') and ((CurPtr + 1)^ in ['x', 'X']) and
((CurPtr + 2)^ in ['a'..'z', 'A'..'Z', '0'..'9'])
then begin
inc(TokenEndPtr, 2);
while TokenEndPtr^ in ['a'..'z', 'A'..'Z', '0'..'9'] do inc(TokenEndPtr);
end
else begin
while TokenEndPtr^ in ['0'..'9'] do inc(TokenEndPtr);
// identify "2.", but not "[2..3]" // CurExpr.IsFloatAllowed
if (TokenEndPtr^ = DecimalSeparator) and (TokenEndPtr[1] <> '.') then begin
inc(TokenEndPtr);
while TokenEndPtr^ in ['0'..'9'] do inc(TokenEndPtr);
if TokenEndPtr^ in ['a'..'z', 'A'..'Z', '_'] then
SetError(fpErrPasParserUnexpectedToken, [GetFirstToken(CurPtr), PosFromPChar(CurPtr)])
else
AddPart(TFpPascalExpressionPartConstantNumberFloat);
exit;
end;
end;
end;
if TokenEndPtr^ in ['a'..'z', 'A'..'Z', '_'] then
SetError(fpErrPasParserUnexpectedToken, [GetFirstToken(CurPtr), PosFromPChar(CurPtr)])
else
AddPart(TFpPascalExpressionPartConstantNumber);
end;
procedure HandleCompare;
begin
if (CurPtr^ = '<') and (TokenEndPtr^ in ['>', '=']) then
inc(TokenEndPtr);
if (CurPtr^ = '>') and (TokenEndPtr^ in ['<', '=']) then
inc(TokenEndPtr);
AddPart(TFpPascalExpressionPartOperatorCompare);
end;
procedure HandleComma;
begin
if not CurPart.HandleSeparator(ppstComma) then
SetError(fpErrPasParserUnexpectedToken, [GetFirstToken(CurPtr), PosFromPChar(CurPtr)]);
end;
procedure AddConstChar;
begin
SetError(Format('Unexpected char ''%0:s'' at pos %1:d', [CurPtr^, PosFromPChar(CurPtr)])); // error
end;
begin
if FTextExpression = '' then
exit;
CurPtr := @FTextExpression[1];
EndPtr := CurPtr + length(FTextExpression);
CurPart := nil;
While (CurPtr < EndPtr) and FValid do begin
if CurPtr^ in [' ', #9, #10, #13] then begin
while (CurPtr^ in [' ', #9, #10, #13]) and (CurPtr < EndPtr) do
Inc(CurPtr);
continue;
end;
NewPart := nil;
TokenEndPtr := CurPtr + 1;
case CurPtr^ of
'@' : AddPart(TFpPascalExpressionPartOperatorAddressOf);
'^': AddRefOperator; // ^A may be #$01
'.': HandleDot;
'+', '-' : AddPlusMinus;
'*', '/' : AddPart(TFpPascalExpressionPartOperatorMulDiv);
'(': HandleRoundBracket;
')': CloseBracket(TFpPascalExpressionPartRoundBracket);
'[': HandleSqareBracket;
']': CloseBracket(TFpPascalExpressionPartSquareBracket);
',': HandleComma;
'=', '<',
'>': HandleCompare;//TFpPascalExpressionPartOperatorCompare
'''', '#': AddConstChar;
'0'..'9',
'$', '%', '&': AddConstNumber;
'a'..'z',
'A'..'Z', '_': AddIdentifier;
else begin
//SetError(fpErrPasParserUnexpectedToken, [GetFirstToken(CurPtr), PosFromPChar(CurPtr)])
SetError(Format('Unexpected token ''%0:s'' at pos %1:d', [CurPtr^, PosFromPChar(CurPtr)])); // error
break;
end;
end;
if not FValid then
break;
if CurPart = nil then
CurPart := NewPart
else
if NewPart <> nil then
CurPart := CurPart.HandleNextPart(NewPart);
CurPtr := TokenEndPtr;
end; // While CurPtr < EndPtr do begin
if Valid then begin
if CurPart <> nil then begin
CurPart.HandleEndOfExpression;
CurPart := CurPart.TopParent;
end
else
SetError('No Expression');
end
else
if CurPart <> nil then
CurPart := CurPart.TopParent;
FExpressionPart := CurPart;
end;
function TFpPascalExpression.GetResultValue: TFpDbgValue;
begin
if (FExpressionPart = nil) or (not Valid) then
Result := nil
else begin
Result := FExpressionPart.ResultValue;
if (Result = nil) and (not IsError(FError)) then
SetError(fpErrAnyError, ['Internal eval error']);
end;
end;
function TFpPascalExpression.GetValid: Boolean;
begin
Result := FValid and (not IsError(FError));
end;
procedure TFpPascalExpression.SetError(AMsg: String);
begin
if IsError(FError) then begin
DebugLn(['Skipping error ', AMsg]);
FValid := False;
exit;
end;
SetError(fpErrAnyError, [AMsg]);
DebugLn(['PARSER ERROR ', AMsg]);
end;
procedure TFpPascalExpression.SetError(AnErrorCode: TFpErrorCode; AData: array of const);
begin
FValid := False;
FError := ErrorHandler.CreateError(AnErrorCode, AData);
DebugLn(['Setting error ', ErrorHandler.ErrorAsString(FError)]);
end;
function TFpPascalExpression.PosFromPChar(APChar: PChar): Integer;
begin
Result := APChar - @FTextExpression[1] + 1;
end;
function TFpPascalExpression.GetDbgSymbolForIdentifier(AnIdent: String): TFpDbgValue;
begin
if FContext <> nil then
Result := FContext.FindSymbol(AnIdent)
else
Result := nil;
end;
constructor TFpPascalExpression.Create(ATextExpression: String;
AContext: TFpDbgInfoContext);
begin
FContext := AContext;
FTextExpression := ATextExpression;
FError := NoError;
FValid := True;
Parse;
end;
destructor TFpPascalExpression.Destroy;
begin
FreeAndNil(FExpressionPart);
inherited Destroy;
end;
function TFpPascalExpression.DebugDump(AWithResults: Boolean): String;
begin
Result := 'TFpPascalExpression: ' + FTextExpression + LineEnding +
'Valid: ' + dbgs(FValid) + ' Error: "' + dbgs(ErrorCode(FError)) + '"'+ LineEnding
;
if FExpressionPart <> nil then
Result := Result + FExpressionPart.DebugDump(' ', AWithResults);
if AWithResults and (ResultValue <> nil) then
if (ResultValue is TFpPasParserValue) then
Result := Result + 'ResultValue = ' + LineEnding + TFpPasParserValue(ResultValue).DebugText(' ')
else
Result := Result + 'ResultValue = ' + LineEnding + DbgSName(ResultValue) + LineEnding ;
end;
procedure TFpPascalExpression.ResetEvaluation;
begin
FExpressionPart.ResetEvaluation;
end;
{ TFpPascalExpressionPart }
procedure TFpPascalExpressionPart.SetEndChar(AValue: PChar);
begin
if FEndChar = AValue then Exit;
FEndChar := AValue;
end;
function TFpPascalExpressionPart.GetTopParent: TFpPascalExpressionPart;
begin
Result := Self;
while Result.Parent <> nil do
Result := Result.Parent;
end;
function TFpPascalExpressionPart.GetSurroundingOpenBracket: TFpPascalExpressionPartBracket;
var
tmp: TFpPascalExpressionPart;
begin
Result := nil;
tmp := Self;
while (tmp <> nil) and
( not(tmp is TFpPascalExpressionPartBracket) or ((tmp as TFpPascalExpressionPartBracket).IsClosed) )
do
tmp := tmp.Parent;
if tmp <> nil then
Result := TFpPascalExpressionPartBracket(tmp);
end;
function TFpPascalExpressionPart.GetResultValue: TFpDbgValue;
begin
Result := FResultValue;
if FResultValDone then
exit;
FResultValue := DoGetResultValue;
{$IFDEF WITH_REFCOUNT_DEBUG}if FResultValue <> nil then FResultValue.DbgRenameReference(nil, 'DoGetResultValue', @FResultValue, 'DoGetResultValue');{$ENDIF}
FResultValDone := True;
Result := FResultValue;
end;
procedure TFpPascalExpressionPart.SetParent(AValue: TFpPascalExpressionPartContainer);
begin
if FParent = AValue then Exit;
FParent := AValue;
end;
procedure TFpPascalExpressionPart.SetStartChar(AValue: PChar);
begin
if FStartChar = AValue then Exit;
FStartChar := AValue;
end;
function TFpPascalExpressionPart.GetText(AMaxLen: Integer): String;
var
Len: Integer;
begin
if FEndChar <> nil
then Len := FEndChar - FStartChar + 1
else Len := min(AMaxLen, 10);
if (AMaxLen > 0) and (Len > AMaxLen) then
Len := AMaxLen;
Result := Copy(FStartChar, 1, Len);
end;
procedure TFpPascalExpressionPart.SetError(AMsg: String);
begin
if AMsg = '' then
AMsg := 'Invalid Expression';
FExpression.SetError(Format('%0:s at %1:d: "%2:s"', [AMsg, FExpression.PosFromPChar(FStartChar), GetText(20)]));
end;
procedure TFpPascalExpressionPart.SetError(APart: TFpPascalExpressionPart; AMsg: String);
begin
if APart <> nil
then APart.SetError(AMsg)
else Self.SetError(AMsg);
end;
procedure TFpPascalExpressionPart.SetError(AnErrorCode: TFpErrorCode; AData: array of const);
begin
FExpression.SetError(AnErrorCode, AData);
end;
procedure TFpPascalExpressionPart.Init;
begin
//
end;
function TFpPascalExpressionPart.DoGetIsTypeCast: Boolean;
begin
Result := False;
end;
function TFpPascalExpressionPart.DoGetResultValue: TFpDbgValue;
begin
Result := nil;
end;
procedure TFpPascalExpressionPart.ResetEvaluation;
begin
FResultValue.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FResultValue, 'DoGetResultValue'){$ENDIF};
FResultValue := nil;
FResultValDone := False;
end;
procedure TFpPascalExpressionPart.ReplaceInParent(AReplacement: TFpPascalExpressionPart);
var
i: Integer;
begin
if Parent = nil then exit;
i := Parent.IndexOf(Self);
Assert(i >= 0);
Parent.Items[i] := AReplacement;
Parent := nil;
end;
procedure TFpPascalExpressionPart.DoHandleEndOfExpression;
begin
//
end;
function TFpPascalExpressionPart.IsValidNextPart(APart: TFpPascalExpressionPart): Boolean;
begin
Result := APart.IsValidAfterPart(Self);
end;
function TFpPascalExpressionPart.IsValidAfterPart(APrevPart: TFpPascalExpressionPart): Boolean;
begin
Result := True;
end;
function TFpPascalExpressionPart.MaybeHandlePrevPart(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean;
begin
Result := False;
end;
function TFpPascalExpressionPart.HasPrecedence: Boolean;
begin
Result := False;
end;
function TFpPascalExpressionPart.FindLeftSideOperandByPrecedence(AnOperator: TFpPascalExpressionPartWithPrecedence): TFpPascalExpressionPart;
begin
Result := Self;
end;
function TFpPascalExpressionPart.CanHaveOperatorAsNext: Boolean;
begin
Result := True;
end;
function TFpPascalExpressionPart.HandleSeparator(ASeparatorType: TSeparatorType): Boolean;
begin
Result := (Parent <> nil) and Parent.HandleSeparator(ASeparatorType);
end;
function TFpPascalExpressionPart.DebugText(AIndent: String; AWithResults: Boolean): String;
begin
Result := Format('%s%s at %d: "%s"',
[AIndent, ClassName, FExpression.PosFromPChar(FStartChar), GetText])
+ LineEnding;
end;
function TFpPascalExpressionPart.DebugDump(AIndent: String; AWithResults: Boolean): String;
begin
Result := DebugText(AIndent, AWithResults);
if AWithResults and (FResultValue <> nil) then
if (FResultValue is TFpPasParserValue) then
Result := Result + TFpPasParserValue(FResultValue).DebugText(AIndent+' // ')
else
Result := Result + AIndent+' // FResultValue = ' + DbgSName(FResultValue) + LineEnding;
end;
constructor TFpPascalExpressionPart.Create(AExpression: TFpPascalExpression; AStartChar: PChar;
AnEndChar: PChar);
begin
FExpression := AExpression;
FStartChar := AStartChar;
FEndChar := AnEndChar;
//FResultTypeFlag := rtUnknown;
FResultValDone := False;
Init;
end;
destructor TFpPascalExpressionPart.Destroy;
begin
inherited Destroy;
//FResultType.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(nil, 'DoGetResultType'){$ENDIF};
FResultValue.ReleaseReference{$IFDEF WITH_REFCOUNT_DEBUG}(@FResultValue, 'DoGetResultValue'){$ENDIF};
end;
function TFpPascalExpressionPart.HandleNextPart(APart: TFpPascalExpressionPart): TFpPascalExpressionPart;
begin
Result := APart;
if APart.MaybeHandlePrevPart(Self, Result) then
exit;
if Parent <> nil then begin
Result := Parent.HandleNextPart(APart);
exit;
end;
SetError(APart, 'Unexpected ');
APart.Free;
Result := Self;
end;
procedure TFpPascalExpressionPart.HandleEndOfExpression;
begin
DoHandleEndOfExpression;
if Parent <> nil then
Parent.HandleEndOfExpression;
end;
{ TFpPascalExpressionPartContainer }
function TFpPascalExpressionPartContainer.GetItems(AIndex: Integer): TFpPascalExpressionPart;
begin
Result := TFpPascalExpressionPart(FList[AIndex]);
end;
function TFpPascalExpressionPartContainer.GetLastItem: TFpPascalExpressionPart;
begin
if Count > 0 then
Result := Items[Count - 1]
else
Result := nil;
end;
procedure TFpPascalExpressionPartContainer.SetItems(AIndex: Integer;
AValue: TFpPascalExpressionPart);
begin
AValue.Parent := Self;
FList[AIndex] := AValue;
end;
procedure TFpPascalExpressionPartContainer.SetLastItem(AValue: TFpPascalExpressionPart);
begin
assert(Count >0);
Items[Count-1] := AValue;
end;
procedure TFpPascalExpressionPartContainer.Init;
begin
FList := TList.Create;
inherited Init;
end;
function TFpPascalExpressionPartContainer.DebugDump(AIndent: String;
AWithResults: Boolean): String;
var
i: Integer;
begin
Result := inherited DebugDump(AIndent, AWithResults);
for i := 0 to Count - 1 do
Result := Result + Items[i].DebugDump(AIndent+' ', AWithResults);
end;
function TFpPascalExpressionPartContainer.GetCount: Integer;
begin
Result := FList.Count;
end;
destructor TFpPascalExpressionPartContainer.Destroy;
begin
Clear;
FreeAndNil(FList);
inherited Destroy;
end;
function TFpPascalExpressionPartContainer.Add(APart: TFpPascalExpressionPart): Integer;
begin
APart.Parent := Self;
Result := FList.Add(APart);
end;
function TFpPascalExpressionPartContainer.IndexOf(APart: TFpPascalExpressionPart): Integer;
begin
Result := Count - 1;
while (Result >= 0) and (Items[Result] <> APart) do
dec(Result);
end;
procedure TFpPascalExpressionPartContainer.Clear;
begin
while Count > 0 do begin
Items[0].Free;
FList.Delete(0);
end;
end;
{ TFpPascalExpressionPartBracket }
function TFpPascalExpressionPartBracket.GetAfterComma: Boolean;
begin
Result := (FAfterComma = Count);
end;
procedure TFpPascalExpressionPartBracket.Init;
begin
inherited Init;
FIsClosed := False;
FIsClosing := False;
FAfterComma := -1;
end;
function TFpPascalExpressionPartBracket.HasPrecedence: Boolean;
begin
Result := False;
end;
procedure TFpPascalExpressionPartBracket.DoHandleEndOfExpression;
begin
if not IsClosed then begin
SetError('Bracket not closed');
exit;
end;
inherited DoHandleEndOfExpression;
end;
function TFpPascalExpressionPartBracket.CanHaveOperatorAsNext: Boolean;
begin
Result := IsClosed;
end;
function TFpPascalExpressionPartBracket.HandleNextPartInBracket(APart: TFpPascalExpressionPart): TFpPascalExpressionPart;
begin
Result := Self;
APart.Free;
SetError('Error in ()');
end;
procedure TFpPascalExpressionPartBracket.SetAfterCommaFlag;
begin
FAfterComma := Count;
end;
procedure TFpPascalExpressionPartBracket.CloseBracket;
begin
if AfterComma then begin
SetError(fpErrPasParserMissingExprAfterComma, [GetText]);
exit;
end;
FIsClosing := True;
if LastItem <> nil then
LastItem.HandleEndOfExpression;
FIsClosing := False;
FIsClosed := True;
end;
function TFpPascalExpressionPartBracket.HandleNextPart(APart: TFpPascalExpressionPart): TFpPascalExpressionPart;
begin
if IsClosed then begin
Result := inherited HandleNextPart(APart);
exit;
end;
if not IsValidNextPart(APart) then begin
SetError(APart, 'Invalid operand in () '+GetText+': ');
Result := self;
APart.Free;
exit;
end;
Result := HandleNextPartInBracket(APart);
end;
procedure TFpPascalExpressionPartBracket.HandleEndOfExpression;
begin
if not FIsClosing then
inherited HandleEndOfExpression;
end;
{ TFpPascalExpressionPartOperator }
function TFpPascalExpressionPartOperator.DebugText(AIndent: String;
AWithResults: Boolean): String;
begin
Result := inherited DebugText(AIndent, AWithResults);
while Result[Length(Result)] in [#10, #13] do SetLength(Result, Length(Result)-1);
Result := Result + ' Precedence:' + dbgs(FPrecedence) +
LineEnding;
end;
function TFpPascalExpressionPartOperator.CanHaveOperatorAsNext: Boolean;
begin
Result := HasAllOperands and LastItem.CanHaveOperatorAsNext;
end;
function TFpPascalExpressionPartOperator.FindLeftSideOperandByPrecedence(AnOperator: TFpPascalExpressionPartWithPrecedence): TFpPascalExpressionPart;
begin
Result := Self;
if (not HasAllOperands) or (LastItem = nil) then begin
Result := nil;
exit
end;
// precedence: 1 = highest
if Precedence > AnOperator.Precedence then
Result := LastItem.FindLeftSideOperandByPrecedence(AnOperator);
end;
function TFpPascalExpressionPartOperator.MaybeAddLeftOperand(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean;
var
ALeftSide: TFpPascalExpressionPart;
begin
Result := APrevPart.IsValidNextPart(Self);
if not Result then
exit;
AResult := Self;
if (Count > 0) or // Previous already set
(not APrevPart.CanHaveOperatorAsNext) // can not have 2 operators follow each other
then begin
SetError(APrevPart, 'Can not apply operator '+GetText+': ');
APrevPart.Free;
exit;
end;
ALeftSide := APrevPart.FindLeftSideOperandByPrecedence(Self);
if ALeftSide = nil then begin
SetError(Self, 'Internal parser error for operator '+GetText+': ');
APrevPart.Free;
exit;
end;
ALeftSide.ReplaceInParent(Self);
Add(ALeftSide);
end;
procedure TFpPascalExpressionPartOperator.DoHandleEndOfExpression;
begin
if not HasAllOperands then
SetError(Self, 'Not enough operands')
else
inherited DoHandleEndOfExpression;
end;
function TFpPascalExpressionPartOperator.HandleSeparator(ASeparatorType: TSeparatorType): Boolean;
begin
Result := HasAllOperands and (inherited HandleSeparator(ASeparatorType));
end;
function TFpPascalExpressionPartOperator.HandleNextPart(APart: TFpPascalExpressionPart): TFpPascalExpressionPart;
begin
Result := Self;
if HasAllOperands then begin
Result := inherited HandleNextPart(APart);
exit;
end;
if not IsValidNextPart(APart) then begin
SetError(APart, 'Not possible after Operator '+GetText+': ');
APart.Free;
exit;
end;
Add(APart);
Result := APart;
end;
{ TFpPascalExpressionPartUnaryOperator }
function TFpPascalExpressionPartUnaryOperator.HasAllOperands: Boolean;
begin
Result := Count = 1;
end;
{ TFpPascalExpressionPartBinaryOperator }
function TFpPascalExpressionPartBinaryOperator.HasAllOperands: Boolean;
begin
Result := Count = 2;
end;
function TFpPascalExpressionPartBinaryOperator.IsValidAfterPart(APrevPart: TFpPascalExpressionPart): Boolean;
begin
Result := inherited IsValidAfterPart(APrevPart);
if not Result then
exit;
Result := APrevPart.CanHaveOperatorAsNext;
// BinaryOperator...
// foo
// Identifier
// "Identifier" can hane a binary-op next. But it must be applied to the parent.
// So it is not valid here.
// If new operator has a higher precedence, it go down to the child again and replace it
if (APrevPart.Parent <> nil) and (APrevPart.Parent.HasPrecedence) then
Result := False;
end;
function TFpPascalExpressionPartBinaryOperator.MaybeHandlePrevPart(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean;
begin
Result := MaybeAddLeftOperand(APrevPart, AResult);
end;
{ TFpPascalExpressionPartOperatorAddressOf }
procedure TFpPascalExpressionPartOperatorAddressOf.Init;
begin
FPrecedence := PRECEDENCE_ADRESS_OF;
inherited Init;
end;
function TFpPascalExpressionPartOperatorAddressOf.DoGetResultValue: TFpDbgValue;
var
tmp: TFpDbgValue;
begin
Result := nil;
if Count <> 1 then exit;
tmp := Items[0].ResultValue;
if (tmp = nil) or not IsTargetAddr(tmp.Address) then
exit;
Result := TFpPasParserValueAddressOf.Create(tmp, Expression.Context);
{$IFDEF WITH_REFCOUNT_DEBUG}Result.DbgRenameReference(nil, 'DoGetResultValue');{$ENDIF}
end;
{ TFpPascalExpressionPartOperatorMakeRef }
procedure TFpPascalExpressionPartOperatorMakeRef.Init;
begin
FPrecedence := PRECEDENCE_MAKE_REF;
inherited Init;
end;
function TFpPascalExpressionPartOperatorMakeRef.IsValidNextPart(APart: TFpPascalExpressionPart): Boolean;
begin
if HasAllOperands then
Result := (inherited IsValidNextPart(APart))
else
Result := (inherited IsValidNextPart(APart)) and
( (APart is TFpPascalExpressionPartIdentifier) or
(APart is TFpPascalExpressionPartOperatorMakeRef)
);
end;
function TFpPascalExpressionPartOperatorMakeRef.DoGetResultValue: TFpDbgValue;
var
tmp: TFpDbgValue;
begin
Result := nil;
if Count <> 1 then exit;
tmp := Items[0].ResultValue;
if tmp = nil then
exit;
if tmp is TFpPasParserValueMakeReftype then begin
TFpPasParserValueMakeReftype(tmp).IncRefLevel;
Result := tmp;
Result.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(nil, 'DoGetResultValue'){$ENDIF};
exit;
end;
if (tmp.DbgSymbol = nil) or (tmp.DbgSymbol.SymbolType <> stType) then
exit;
Result := TFpPasParserValueMakeReftype.Create(tmp.DbgSymbol, Expression.Context);
{$IFDEF WITH_REFCOUNT_DEBUG}Result.DbgRenameReference(nil, 'DoGetResultValue'){$ENDIF};
end;
function TFpPascalExpressionPartOperatorMakeRef.DoGetIsTypeCast: Boolean;
begin
Result := True;
end;
{ TFpPascalExpressionPartOperatorDeRef }
procedure TFpPascalExpressionPartOperatorDeRef.Init;
begin
FPrecedence := PRECEDENCE_DEREF;
inherited Init;
end;
function TFpPascalExpressionPartOperatorDeRef.DoGetResultValue: TFpDbgValue;
var
tmp: TFpDbgValue;
begin
Result := nil;
if Count <> 1 then exit;
tmp := Items[0].ResultValue;
if tmp = nil then
exit;
if tmp is TFpPasParserValueAddressOf then begin // TODO: remove IF, handled in GetMember
Result := TFpPasParserValueAddressOf(tmp).PointedToValue;
Result.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(nil, 'DoGetResultValue'){$ENDIF};
end
else
if tmp.Kind = skPointer then begin
if (svfDataAddress in tmp.FieldFlags) and (IsReadableLoc(tmp.DataAddress)) and // TODO, what if Not readable addr
(tmp.TypeInfo <> nil) //and (tmp.TypeInfo.TypeInfo <> nil)
then begin
Result := tmp.Member[0];
if Result <> nil then
Result.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(nil, 'DoGetResultValue'){$ENDIF};
end;
end
//if tmp.Kind = skArray then // dynarray
else
begin
Result := nil;
SetError(fpErrCannotDereferenceType, [GetText]);
end;
end;
function TFpPascalExpressionPartOperatorDeRef.MaybeHandlePrevPart(APrevPart: TFpPascalExpressionPart;
var AResult: TFpPascalExpressionPart): Boolean;
begin
Result := MaybeAddLeftOperand(APrevPart, AResult);
end;
function TFpPascalExpressionPartOperatorDeRef.FindLeftSideOperandByPrecedence(AnOperator: TFpPascalExpressionPartWithPrecedence): TFpPascalExpressionPart;
begin
Result := Self;
end;
function TFpPascalExpressionPartOperatorDeRef.IsValidAfterPart(APrevPart: TFpPascalExpressionPart): Boolean;
begin
Result := inherited IsValidAfterPart(APrevPart);
if not Result then
exit;
Result := APrevPart.CanHaveOperatorAsNext;
// BinaryOperator...
// foo
// Identifier
// "Identifier" can hane a binary-op next. But it must be applied to the parent.
// So it is not valid here.
// If new operator has a higher precedence, it go down to the child again and replace it
if (APrevPart.Parent <> nil) and (APrevPart.Parent is TFpPascalExpressionPartOperator) then
Result := False;
end;
{ TFpPascalExpressionPartOperatorUnaryPlusMinus }
procedure TFpPascalExpressionPartOperatorUnaryPlusMinus.Init;
begin
FPrecedence := PRECEDENCE_UNARY_SIGN;
inherited Init;
end;
function TFpPascalExpressionPartOperatorUnaryPlusMinus.DoGetResultValue: TFpDbgValue;
var
tmp1: TFpDbgValue;
IsAdd: Boolean;
begin
Result := nil;
if Count <> 1 then exit;
assert((GetText = '+') or (GetText = '-'), 'TFpPascalExpressionPartOperatorUnaryPlusMinus.DoGetResultValue: (GetText = +) or (GetText = -)');
tmp1 := Items[0].ResultValue;
IsAdd := GetText = '+';
if (tmp1 = nil) then exit;
{$PUSH}{$R-}{$Q-}
if IsAdd then begin
case tmp1.Kind of
skPointer: ;
skInteger: Result := tmp1;
skCardinal: Result := tmp1;
end;
end
else begin
case tmp1.Kind of
skPointer: ;
skInteger: Result := TFpDbgValueConstNumber.Create(-tmp1.AsInteger, True);
skCardinal: Result := TFpDbgValueConstNumber.Create(-tmp1.AsCardinal, True);
end;
end;
{$POP}
{$IFDEF WITH_REFCOUNT_DEBUG}if Result <> nil then Result.DbgRenameReference(nil, 'DoGetResultValue');{$ENDIF}
end;
{ TFpPascalExpressionPartOperatorPlusMinus }
procedure TFpPascalExpressionPartOperatorPlusMinus.Init;
begin
FPrecedence := PRECEDENCE_PLUS_MINUS;
inherited Init;
end;
function TFpPascalExpressionPartOperatorPlusMinus.DoGetResultValue: TFpDbgValue;
{$PUSH}{$R-}{$Q-}
function AddSubValueToPointer(APointerVal, AOtherVal: TFpDbgValue; ADoSubtract: Boolean = False): TFpDbgValue;
var
Idx: Int64;
TmpVal: TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
// skPointer: Result := nil;
skInteger: Idx := AOtherVal.AsInteger;
skCardinal: begin
Idx := AOtherVal.AsInteger;
if Idx > High(Int64) then
exit; // TODO: error
end;
else
exit; // TODO: error
end;
if ADoSubtract then begin
if Idx < -(High(Int64)) then
exit; // TODO: error
Idx := -Idx;
end;
TmpVal := APointerVal.Member[Idx];
if IsError(APointerVal.LastError) then begin
SetError('Error dereferencing'); // TODO: set correct error
exit;
end;
Result := TFpPasParserValueAddressOf.Create(TmpVal, Expression.Context);
end;
function AddValueToInt(AIntVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skPointer: Result := AddSubValueToPointer(AOtherVal, AIntVal);
skInteger: Result := TFpDbgValueConstNumber.Create(AIntVal.AsInteger + AOtherVal.AsInteger, True);
skCardinal: Result := TFpDbgValueConstNumber.Create(AIntVal.AsInteger + AOtherVal.AsCardinal, True);
skFloat: Result := TFpDbgValueConstFloat.Create(AIntVal.AsInteger + AOtherVal.AsFloat);
else SetError('Addition not supported');
end;
end;
function AddValueToCardinal(ACardinalVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skPointer: Result := AddSubValueToPointer(AOtherVal, ACardinalVal);
skInteger: Result := TFpDbgValueConstNumber.Create(ACardinalVal.AsCardinal + AOtherVal.AsInteger, True);
skCardinal: Result := TFpDbgValueConstNumber.Create(ACardinalVal.AsCardinal + AOtherVal.AsCardinal, False);
skFloat: Result := TFpDbgValueConstFloat.Create(ACardinalVal.AsCardinal + AOtherVal.AsFloat);
else SetError('Addition not supported');
end;
end;
function AddValueToFloat(AFloatVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat + AOtherVal.AsInteger);
skCardinal: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat + AOtherVal.AsCardinal);
skFloat: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat + AOtherVal.AsFloat);
else SetError('Addition not supported');
end;
end;
function SubPointerFromValue(APointerVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil; // Error
end;
function SubValueFromInt(AIntVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skPointer: Result := SubPointerFromValue(AOtherVal, AIntVal);
skInteger: Result := TFpDbgValueConstNumber.Create(AIntVal.AsInteger - AOtherVal.AsInteger, True);
skCardinal: Result := TFpDbgValueConstNumber.Create(AIntVal.AsInteger - AOtherVal.AsCardinal, True);
skFloat: Result := TFpDbgValueConstFloat.Create(AIntVal.AsInteger - AOtherVal.AsFloat);
else SetError('Subtraction not supported');
end;
end;
function SubValueFromCardinal(ACardinalVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skPointer: Result := SubPointerFromValue(AOtherVal, ACardinalVal);
skInteger: Result := TFpDbgValueConstNumber.Create(ACardinalVal.AsCardinal - AOtherVal.AsInteger, True);
skCardinal: Result := TFpDbgValueConstNumber.Create(ACardinalVal.AsCardinal - AOtherVal.AsCardinal, False);
skFloat: Result := TFpDbgValueConstFloat.Create(ACardinalVal.AsCardinal - AOtherVal.AsFloat);
else SetError('Subtraction not supported');
end;
end;
function SubValueFromFloat(AFloatVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat - AOtherVal.AsInteger);
skCardinal: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat - AOtherVal.AsCardinal);
skFloat: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat - AOtherVal.AsFloat);
else SetError('Subtraction not supported');
end;
end;
{$POP}
var
tmp1, tmp2: TFpDbgValue;
IsAdd: Boolean;
begin
Result := nil;
if Count <> 2 then exit;
assert((GetText = '+') or (GetText = '-'), 'TFpPascalExpressionPartOperatorUnaryPlusMinus.DoGetResultValue: (GetText = +) or (GetText = -)');
tmp1 := Items[0].ResultValue;
tmp2 := Items[1].ResultValue;
IsAdd := GetText = '+';
if (tmp1 = nil) or (tmp2 = nil) then exit;
if IsAdd then begin
case tmp1.Kind of
skPointer: Result := AddSubValueToPointer(tmp1, tmp2);
skInteger: Result := AddValueToInt(tmp1, tmp2);
skCardinal: Result := AddValueToCardinal(tmp1, tmp2);
skFloat: Result := AddValueToFloat(tmp1, tmp2);
end;
end
else begin
case tmp1.Kind of
skPointer: Result := AddSubValueToPointer(tmp1, tmp2, True);
skInteger: Result := SubValueFromInt(tmp1, tmp2);
skCardinal: Result := SubValueFromCardinal(tmp1, tmp2);
skFloat: Result := SubValueFromFloat(tmp1, tmp2);
end;
end;
{$IFDEF WITH_REFCOUNT_DEBUG}if Result <> nil then
Result.DbgRenameReference(nil, 'DoGetResultValue');{$ENDIF}
end;
{ TFpPascalExpressionPartOperatorMulDiv }
procedure TFpPascalExpressionPartOperatorMulDiv.Init;
begin
FPrecedence := PRECEDENCE_MUL_DIV;
inherited Init;
end;
function TFpPascalExpressionPartOperatorMulDiv.DoGetResultValue: TFpDbgValue;
{$PUSH}{$R-}{$Q-}
function MultiplyIntWithValue(AIntVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstNumber.Create(AIntVal.AsInteger * AOtherVal.AsInteger, True);
skCardinal: Result := TFpDbgValueConstNumber.Create(AIntVal.AsInteger * AOtherVal.AsCardinal, True);
skFloat: Result := TFpDbgValueConstFloat.Create(AIntVal.AsInteger * AOtherVal.AsFloat);
else SetError('Multiply not supported');
end;
end;
function MultiplyCardinalWithValue(ACardinalVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstNumber.Create(ACardinalVal.AsCardinal * AOtherVal.AsInteger, True);
skCardinal: Result := TFpDbgValueConstNumber.Create(ACardinalVal.AsCardinal * AOtherVal.AsCardinal, False);
skFloat: Result := TFpDbgValueConstFloat.Create(ACardinalVal.AsCardinal * AOtherVal.AsFloat);
else SetError('Multiply not supported');
end;
end;
function MultiplyFloatWithValue(AFloatVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat * AOtherVal.AsInteger);
skCardinal: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat * AOtherVal.AsCardinal);
skFloat: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat * AOtherVal.AsFloat);
else SetError('Multiply not supported');
end;
end;
function FloatDivIntByValue(AIntVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstFloat.Create(AIntVal.AsInteger / AOtherVal.AsInteger);
skCardinal: Result := TFpDbgValueConstFloat.Create(AIntVal.AsInteger / AOtherVal.AsCardinal);
skFloat: Result := TFpDbgValueConstFloat.Create(AIntVal.AsInteger / AOtherVal.AsFloat);
else SetError('/ not supported');
end;
end;
function FloatDivCardinalByValue(ACardinalVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstFloat.Create(ACardinalVal.AsCardinal / AOtherVal.AsInteger);
skCardinal: Result := TFpDbgValueConstFloat.Create(ACardinalVal.AsCardinal / AOtherVal.AsCardinal);
skFloat: Result := TFpDbgValueConstFloat.Create(ACardinalVal.AsCardinal / AOtherVal.AsFloat);
else SetError('/ not supported');
end;
end;
function FloatDivFloatByValue(AFloatVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat / AOtherVal.AsInteger);
skCardinal: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat / AOtherVal.AsCardinal);
skFloat: Result := TFpDbgValueConstFloat.Create(AFloatVal.AsFloat / AOtherVal.AsFloat);
else SetError('/ not supported');
end;
end;
function NumDivIntByValue(AIntVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstNumber.Create(AIntVal.AsInteger div AOtherVal.AsInteger, True);
skCardinal: Result := TFpDbgValueConstNumber.Create(AIntVal.AsInteger div AOtherVal.AsCardinal, True);
else SetError('Div not supported');
end;
end;
function NumDivCardinalByValue(ACardinalVal, AOtherVal: TFpDbgValue): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstNumber.Create(ACardinalVal.AsCardinal div AOtherVal.AsInteger, True);
skCardinal: Result := TFpDbgValueConstNumber.Create(ACardinalVal.AsCardinal div AOtherVal.AsCardinal, False);
else SetError('Div not supported');
end;
end;
{$POP}
var
tmp1, tmp2: TFpDbgValue;
begin
Result := nil;
if Count <> 2 then exit;
assert((GetText = '*') or (GetText = '/') or (LowerCase(GetText) = 'div') , 'TFpPascalExpressionPartOperatorUnaryPlusMinus.DoGetResultValue: (GetText = +) or (GetText = -)');
tmp1 := Items[0].ResultValue;
tmp2 := Items[1].ResultValue;
if (tmp1 = nil) or (tmp2 = nil) then exit;
if GetText = '*' then begin
case tmp1.Kind of
skInteger: Result := MultiplyIntWithValue(tmp1, tmp2);
skCardinal: Result := MultiplyCardinalWithValue(tmp1, tmp2);
skFloat: Result := MultiplyFloatWithValue(tmp1, tmp2);
end;
end
else
if GetText = '/' then begin
case tmp1.Kind of
skInteger: Result := FloatDivIntByValue(tmp1, tmp2);
skCardinal: Result := FloatDivCardinalByValue(tmp1, tmp2);
skFloat: Result := FloatDivFloatByValue(tmp1, tmp2);
end;
end
else
if LowerCase(GetText) = 'div' then begin
case tmp1.Kind of
skInteger: Result := NumDivIntByValue(tmp1, tmp2);
skCardinal: Result := NumDivCardinalByValue(tmp1, tmp2);
end;
end;
{$IFDEF WITH_REFCOUNT_DEBUG}if Result <> nil then
Result.DbgRenameReference(nil, 'DoGetResultValue');{$ENDIF}
end;
{ TFpPascalExpressionPartOperatorCompare }
procedure TFpPascalExpressionPartOperatorCompare.Init;
begin
FPrecedence := PRECEDENCE_COMPARE;
inherited Init;
end;
function TFpPascalExpressionPartOperatorCompare.DoGetResultValue: TFpDbgValue;
{$PUSH}{$R-}{$Q-}
function IntEqualToValue(AIntVal, AOtherVal: TFpDbgValue; ARevert: Boolean = False): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstBool.Create((AIntVal.AsInteger = AOtherVal.AsInteger) xor ARevert);
skCardinal: Result := TFpDbgValueConstBool.Create((AIntVal.AsInteger = AOtherVal.AsCardinal) xor ARevert);
skFloat: Result := TFpDbgValueConstBool.Create((AIntVal.AsInteger = AOtherVal.AsFloat) xor ARevert);
else SetError('= not supported');
end;
end;
function CardinalEqualToValue(ACardinalVal, AOtherVal: TFpDbgValue; ARevert: Boolean = False): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstBool.Create((ACardinalVal.AsCardinal = AOtherVal.AsInteger) xor ARevert);
skCardinal: Result := TFpDbgValueConstBool.Create((ACardinalVal.AsCardinal = AOtherVal.AsCardinal) xor ARevert);
skFloat: Result := TFpDbgValueConstBool.Create((ACardinalVal.AsCardinal = AOtherVal.AsFloat) xor ARevert);
else SetError('= not supported');
end;
end;
function FloatEqualToValue(AFloatVal, AOtherVal: TFpDbgValue; ARevert: Boolean = False): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstBool.Create((AFloatVal.AsFloat = AOtherVal.AsInteger) xor ARevert);
skCardinal: Result := TFpDbgValueConstBool.Create((AFloatVal.AsFloat = AOtherVal.AsCardinal) xor ARevert);
skFloat: Result := TFpDbgValueConstBool.Create((AFloatVal.AsFloat = AOtherVal.AsFloat) xor ARevert);
else SetError('= not supported');
end;
end;
function IntGreaterThanValue(AIntVal, AOtherVal: TFpDbgValue; ARevert: Boolean = False): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstBool.Create((AIntVal.AsInteger > AOtherVal.AsInteger) xor ARevert);
skCardinal: Result := TFpDbgValueConstBool.Create((AIntVal.AsInteger > AOtherVal.AsCardinal) xor ARevert);
skFloat: Result := TFpDbgValueConstBool.Create((AIntVal.AsInteger > AOtherVal.AsFloat) xor ARevert);
else SetError('= not supported');
end;
end;
function CardinalGreaterThanValue(ACardinalVal, AOtherVal: TFpDbgValue; ARevert: Boolean = False): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstBool.Create((ACardinalVal.AsCardinal > AOtherVal.AsInteger) xor ARevert);
skCardinal: Result := TFpDbgValueConstBool.Create((ACardinalVal.AsCardinal > AOtherVal.AsCardinal) xor ARevert);
skFloat: Result := TFpDbgValueConstBool.Create((ACardinalVal.AsCardinal > AOtherVal.AsFloat) xor ARevert);
else SetError('= not supported');
end;
end;
function FloatGreaterThanValue(AFloatVal, AOtherVal: TFpDbgValue; ARevert: Boolean = False): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstBool.Create((AFloatVal.AsFloat > AOtherVal.AsInteger) xor ARevert);
skCardinal: Result := TFpDbgValueConstBool.Create((AFloatVal.AsFloat > AOtherVal.AsCardinal) xor ARevert);
skFloat: Result := TFpDbgValueConstBool.Create((AFloatVal.AsFloat > AOtherVal.AsFloat) xor ARevert);
else SetError('= not supported');
end;
end;
function IntSmallerThanValue(AIntVal, AOtherVal: TFpDbgValue; ARevert: Boolean = False): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstBool.Create((AIntVal.AsInteger < AOtherVal.AsInteger) xor ARevert);
skCardinal: Result := TFpDbgValueConstBool.Create((AIntVal.AsInteger < AOtherVal.AsCardinal) xor ARevert);
skFloat: Result := TFpDbgValueConstBool.Create((AIntVal.AsInteger < AOtherVal.AsFloat) xor ARevert);
else SetError('= not supported');
end;
end;
function CardinalSmallerThanValue(ACardinalVal, AOtherVal: TFpDbgValue; ARevert: Boolean = False): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstBool.Create((ACardinalVal.AsCardinal < AOtherVal.AsInteger) xor ARevert);
skCardinal: Result := TFpDbgValueConstBool.Create((ACardinalVal.AsCardinal < AOtherVal.AsCardinal) xor ARevert);
skFloat: Result := TFpDbgValueConstBool.Create((ACardinalVal.AsCardinal < AOtherVal.AsFloat) xor ARevert);
else SetError('= not supported');
end;
end;
function FloatSmallerThanValue(AFloatVal, AOtherVal: TFpDbgValue; ARevert: Boolean = False): TFpDbgValue;
begin
Result := nil;
case AOtherVal.Kind of
skInteger: Result := TFpDbgValueConstBool.Create((AFloatVal.AsFloat < AOtherVal.AsInteger) xor ARevert);
skCardinal: Result := TFpDbgValueConstBool.Create((AFloatVal.AsFloat < AOtherVal.AsCardinal) xor ARevert);
skFloat: Result := TFpDbgValueConstBool.Create((AFloatVal.AsFloat < AOtherVal.AsFloat) xor ARevert);
else SetError('= not supported');
end;
end;
{$POP}
var
tmp1, tmp2: TFpDbgValue;
s: String;
begin
Result := nil;
if Count <> 2 then exit;
tmp1 := Items[0].ResultValue;
tmp2 := Items[1].ResultValue;
if (tmp1 = nil) or (tmp2 = nil) then exit;
s := GetText;
if (s = '=') or (s = '<>') then begin
case tmp1.Kind of
skInteger: Result := IntEqualToValue(tmp1, tmp2, (s = '<>'));
skCardinal: Result := CardinalEqualToValue(tmp1, tmp2, (s = '<>'));
skFloat: Result := FloatEqualToValue(tmp1, tmp2, (s = '<>'));
end;
end
else
if (s = '>') or (s = '<=') then begin
case tmp1.Kind of
skInteger: Result := IntGreaterThanValue(tmp1, tmp2, (s = '<='));
skCardinal: Result := CardinalGreaterThanValue(tmp1, tmp2, (s = '<='));
skFloat: Result := FloatGreaterThanValue(tmp1, tmp2, (s = '<='));
end;
end
else
if (s = '<') or (s = '>=') then begin
case tmp1.Kind of
skInteger: Result := IntSmallerThanValue(tmp1, tmp2, (s = '>='));
skCardinal: Result := CardinalSmallerThanValue(tmp1, tmp2, (s = '>='));
skFloat: Result := FloatSmallerThanValue(tmp1, tmp2, (s = '>='));
end;
end
else
if GetText = '><' then begin
// compare SET
end;
{$IFDEF WITH_REFCOUNT_DEBUG}if Result <> nil then
Result.DbgRenameReference(nil, 'DoGetResultValue');{$ENDIF}
end;
{ TFpPascalExpressionPartOperatorMemberOf }
procedure TFpPascalExpressionPartOperatorMemberOf.Init;
begin
FPrecedence := PRECEDENCE_MEMBER_OF;
inherited Init;
end;
function TFpPascalExpressionPartOperatorMemberOf.IsValidNextPart(APart: TFpPascalExpressionPart): Boolean;
begin
Result := inherited IsValidNextPart(APart);
if not HasAllOperands then
Result := Result and (APart is TFpPascalExpressionPartIdentifier);
end;
function TFpPascalExpressionPartOperatorMemberOf.DoGetResultValue: TFpDbgValue;
var
tmp: TFpDbgValue;
begin
Result := nil;
if Count <> 2 then exit;
tmp := Items[0].ResultValue;
if (tmp = nil) then exit;
if (tmp.Kind in [skClass, skRecord, skObject]) then begin
Result := tmp.MemberByName[Items[1].GetText];
if Result = nil then begin
SetError(fpErrNoMemberWithName, [Items[1].GetText]);
exit;
end;
Result.AddReference{$IFDEF WITH_REFCOUNT_DEBUG}(nil, 'DoGetResultValue'){$ENDIF};
Assert((Result.DbgSymbol=nil)or(Result.DbgSymbol.SymbolType=stValue), 'member is value');
exit;
end;
// Todo unit
SetError(fpErrorNotAStructure, [Items[1].GetText, Items[0].GetText]);
end;
end.