Files
lazarus-ccr/components/fpspreadsheet/fpsexprparser.pas

3665 lines
103 KiB
ObjectPascal

{
This file is part of the Free Component Library (FCL)
Copyright (c) 2008 Michael Van Canneyt.
Expression parser, supports variables, functions and
float/integer/string/boolean/datetime operations.
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program 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.
Modified for integration into fpspreadsheet by Werner Pamler:
- Original file name: fpexprpars.pp
- Rename identifiers to avoid naming conflicts with the original
- TsExpressionParser and TsBuiltinExpressionManager are not components
any more
- TsExpressionParser is created with the worksheet as a parameter.
- add new TExprNode classes:
- TsCellExprNode for references to cells
- TsCellRangeExprNode for references to cell ranges
- TsPercentExprNode and token "%" to handle Excel's percent operation
- TsParenthesisExprNode to handle the parenthesis token in RPN formulas
- TsConcatExprNode and token "&" to handle string concatenation
- remove and modifiy built-in function such that the parser is compatible
with Excel syntax (and OpenOffice - which is the same).
- use double quotes for strings (instead of single quotes)
******************************************************************************}
{$mode objfpc}
{$h+}
unit fpsExprParser;
interface
uses
Classes, SysUtils, contnrs, fpspreadsheet;
type
{ Tokens }
(* { Basic operands }
fekCell, fekCellRef, fekCellRange, fekCellOffset, fekNum, fekInteger,
fekString, fekBool, fekErr, fekMissingArg,
{ Basic operations }
fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
fekConcat, // string concatenation
fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, fekNotEqual,
fekParen,
*)
TTokenType = (
ttPlus, ttMinus, ttMul, ttDiv, ttConcat, ttPercent, ttPower, ttLeft, ttRight,
ttLessThan, ttLargerThan, ttEqual, ttNotEqual, ttLessThanEqual, ttLargerThanEqual,
ttNumber, ttString, ttIdentifier, ttCell, ttCellRange,
ttComma, ttAnd, ttOr, ttXor, ttTrue, ttFalse, ttNot, ttIf,
ttEOF
);
TExprFloat = Double;
const
ttDelimiters = [
ttPlus, ttMinus, ttMul, ttDiv, ttLeft, ttRight, ttLessThan, ttLargerThan,
ttEqual, ttNotEqual, ttLessThanEqual, ttLargerThanEqual
];
ttComparisons = [
ttLargerThan, ttLessThan, ttLargerThanEqual, ttLessThanEqual, ttEqual, ttNotEqual
];
type
TsExpressionParser = class;
TsBuiltInExpressionManager = class;
{ TsExpressionScanner }
TsExpressionScanner = class(TObject)
FSource : String;
LSource,
FPos: Integer;
FChar: PChar;
FToken: String;
FTokenType: TTokenType;
private
function GetCurrentChar: Char;
procedure ScanError(Msg: String);
protected
procedure SetSource(const AValue: String); virtual;
function DoIdentifier: TTokenType;
function DoNumber: TTokenType;
function DoDelimiter: TTokenType;
function DoString: TTokenType;
function NextPos: Char; // inline;
procedure SkipWhiteSpace; // inline;
function IsWordDelim(C: Char): Boolean; // inline;
function IsDelim(C: Char): Boolean; // inline;
function IsDigit(C: Char): Boolean; // inline;
function IsAlpha(C: Char): Boolean; // inline;
public
constructor Create;
function GetToken: TTokenType;
property Token: String read FToken;
property TokenType: TTokenType read FTokenType;
property Source: String read FSource write SetSource;
property Pos: Integer read FPos;
property CurrentChar: Char read GetCurrentChar;
end;
EExprScanner = class(Exception);
TsResultType = (rtBoolean, rtInteger, rtFloat, rtDateTime, rtString);
TsResultTypes = set of TsResultType;
TsExpressionResult = record
ResString : String;
case ResultType : TsResultType of
rtBoolean : (ResBoolean : Boolean);
rtInteger : (ResInteger : Int64);
rtFloat : (ResFloat : TExprFloat);
rtDateTime : (ResDateTime : TDatetime);
rtString : ();
end;
PsExpressionResult = ^TsExpressionResult;
TExprParameterArray = array of TsExpressionResult;
{ TsExprNode }
TsExprNode = class(TObject)
protected
procedure CheckNodeType(ANode: TsExprNode; Allowed: TsResultTypes);
// A procedure with var saves an implicit try/finally in each node
// A marked difference in execution speed.
procedure GetNodeValue(var Result: TsExpressionResult); virtual; abstract;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; virtual; abstract;
function AsString: string; virtual; abstract;
procedure Check; virtual; abstract;
function NodeType: TsResultType; virtual; abstract;
function NodeValue: TsExpressionResult;
end;
TExprArgumentArray = array of TsExprNode;
{ TsBinaryOperationExprNode }
TsBinaryOperationExprNode = class(TsExprNode)
private
FLeft: TsExprNode;
FRight: TsExprNode;
protected
procedure CheckSameNodeTypes;
public
constructor Create(ALeft, ARight: TsExprNode);
destructor Destroy; override;
procedure Check; override;
property Left: TsExprNode read FLeft;
property Right: TsExprNode read FRight;
end;
TsBinaryOperationExprNodeClass = class of TsBinaryOperationExprNode;
{ TsBooleanOperationExprNode }
TsBooleanOperationExprNode = class(TsBinaryOperationExprNode)
public
procedure Check; override;
function NodeType: TsResultType; override;
end;
{ TsBinaryAndExprNode }
TsBinaryAndExprNode = class(TsBooleanOperationExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
end;
{ TsBinaryOrExprNode }
TsBinaryOrExprNode = class(TsBooleanOperationExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
end;
{ TsBinaryXorExprNode }
TsBinaryXorExprNode = class(TsBooleanOperationExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
end;
{ TsBooleanResultExprNode }
TsBooleanResultExprNode = class(TsBinaryOperationExprNode)
public
procedure Check; override;
function NodeType: TsResultType; override;
end;
TsBooleanResultExprNodeClass = class of TsBooleanResultExprNode;
{ TsEqualExprNode }
TsEqualExprNode = class(TsBooleanResultExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
end;
{ TsNotEqualExprNode }
TsNotEqualExprNode = class(TsEqualExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
end;
{ TsOrderingExprNode }
TsOrderingExprNode = class(TsBooleanResultExprNode)
procedure Check; override;
end;
{ TsLessThanExprNode }
TsLessThanExprNode = class(TsOrderingExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
end;
{ TsGreaterThanExprNode }
TsGreaterThanExprNode = class(TsOrderingExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
end;
{ TsLessEqualExprNode }
TsLessEqualExprNode = class(TsGreaterThanExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
end;
{ TsGreaterEqualExprNode }
TsGreaterEqualExprNode = class(TsLessThanExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
end;
{ TIfExprNode }
TIfExprNode = class(TsBinaryOperationExprNode)
private
FCondition: TsExprNode;
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
procedure Check; override;
function NodeType: TsResultType; override;
public
constructor Create(ACondition, ALeft, ARight: TsExprNode);
destructor Destroy; override;
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
property Condition: TsExprNode read FCondition;
end;
{ TsConcatExprNode }
TsConcatExprNode = class(TsBinaryOperationExprNode)
protected
procedure Check; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
function NodeType: TsResultType; override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string ; override;
end;
{ TsMathOperationExprNode }
TsMathOperationExprNode = class(TsBinaryOperationExprNode)
protected
procedure Check; override;
function NodeType: TsResultType; override;
end;
{ TsAddExprNode }
TsAddExprNode = class(TsMathOperationExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string ; override;
end;
{ TsSubtractExprNode }
TsSubtractExprNode = class(TsMathOperationExprNode)
protected
procedure Check; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string ; override;
end;
{ TsMultiplyExprNode }
TsMultiplyExprNode = class(TsMathOperationExprNode)
protected
procedure check; override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string ; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsDivideExprNode }
TsDivideExprNode = class(TsMathOperationExprNode)
protected
Procedure Check; override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string ; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
function NodeType: TsResultType; override;
end;
{ TsUnaryOperationExprNode }
TsUnaryOperationExprNode = class(TsExprNode)
private
FOperand: TsExprNode;
public
constructor Create(AOperand: TsExprNode);
destructor Destroy; override;
procedure Check; override;
property Operand: TsExprNode read FOperand;
end;
{ TsConvertExprNode }
TsConvertExprNode = class(TsUnaryOperationExprNode)
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
end;
{ TsNotExprNode }
TsNotExprNode = class(TsUnaryOperationExprNode)
protected
procedure Check; override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
function NodeType: TsResultType; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsConvertToIntExprNode }
TsConvertToIntExprNode = class(TsConvertExprNode)
protected
procedure Check; override;
end;
{ TsIntToFloatExprNode }
TsIntToFloatExprNode = class(TsConvertToIntExprNode)
public
function NodeType: TsResultType; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsIntToDateTimeExprNode }
TsIntToDateTimeExprNode = class(TsConvertToIntExprNode)
public
function NodeType: TsResultType; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsFloatToDateTimeExprNode }
TsFloatToDateTimeExprNode = class(TsConvertExprNode)
protected
procedure Check; override;
public
function NodeType: TsResultType; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsNegateExprNode }
TsNegateExprNode = class(TsUnaryOperationExprNode)
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
procedure Check; override;
function NodeType: TsResultType; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsPercentExprNode }
TsPercentExprNode = class(TsUnaryOperationExprNode)
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
procedure Check; override;
function NodeType: TsResultType; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsParenthesisExprNode }
TsParenthesisExprNode = class(TsUnaryOperationExprNode)
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
function NodeType: TsResultType; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsConstExprNode }
TsConstExprNode = Class(TsExprNode)
private
FValue: TsExpressionResult;
public
constructor CreateString(AValue: String);
constructor CreateInteger(AValue: Int64);
constructor CreateDateTime(AValue: TDateTime);
constructor CreateFloat(AValue: TExprFloat);
constructor CreateBoolean(AValue: Boolean);
function AsString: string; override;
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
procedure Check; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
function NodeType : TsResultType; override;
// For inspection
property ConstValue: TsExpressionResult read FValue;
end;
TsExprIdentifierType = (itVariable, itFunctionCallBack, itFunctionHandler);
TsExprFunctionCallBack = procedure (var Result: TsExpressionResult;
const Args: TExprParameterArray);
TsExprFunctionEvent = procedure (var Result: TsExpressionResult;
const Args: TExprParameterArray) of object;
{ TsExprIdentifierDef }
TsExprIdentifierDef = class(TCollectionItem)
private
FStringValue: String;
FValue: TsExpressionResult;
FArgumentTypes: String;
FIDType: TsExprIdentifierType;
FName: ShortString;
FOnGetValue: TsExprFunctionEvent;
FOnGetValueCB: TsExprFunctionCallBack;
function GetAsBoolean: Boolean;
function GetAsDateTime: TDateTime;
function GetAsFloat: TExprFloat;
function GetAsInteger: Int64;
function GetAsString: String;
function GetResultType: TsResultType;
function GetValue: String;
procedure SetArgumentTypes(const AValue: String);
procedure SetAsBoolean(const AValue: Boolean);
procedure SetAsDateTime(const AValue: TDateTime);
procedure SetAsFloat(const AValue: TExprFloat);
procedure SetAsInteger(const AValue: Int64);
procedure SetAsString(const AValue: String);
procedure SetName(const AValue: ShortString);
procedure SetResultType(const AValue: TsResultType);
procedure SetValue(const AValue: String);
protected
procedure CheckResultType(const AType: TsResultType);
procedure CheckVariable;
public
function ArgumentCount: Integer;
procedure Assign(Source: TPersistent); override;
property AsFloat: TExprFloat Read GetAsFloat Write SetAsFloat;
property AsInteger: Int64 Read GetAsInteger Write SetAsInteger;
property AsString: String Read GetAsString Write SetAsString;
property AsBoolean: Boolean Read GetAsBoolean Write SetAsBoolean;
property AsDateTime: TDateTime Read GetAsDateTime Write SetAsDateTime;
property OnGetFunctionValueCallBack: TsExprFunctionCallBack read FOnGetValueCB write FOnGetValueCB;
published
property IdentifierType: TsExprIdentifierType read FIDType write FIDType;
property Name: ShortString read FName write SetName;
property Value: String read GetValue write SetValue;
property ParameterTypes: String read FArgumentTypes write SetArgumentTypes;
property ResultType: TsResultType read GetResultType write SetResultType;
property OnGetFunctionValue: TsExprFunctionEvent read FOnGetValue write FOnGetValue;
end;
TsBuiltInExprCategory = (bcStrings, bcDateTime, bcMath, bcBoolean, bcConversion,
bcData, bcVaria, bcUser);
TsBuiltInExprCategories = set of TsBuiltInExprCategory;
{ TsBuiltInExprIdentifierDef }
TsBuiltInExprIdentifierDef = class(TsExprIdentifierDef)
private
FCategory: TsBuiltInExprCategory;
public
procedure Assign(Source: TPersistent); override;
published
property Category: TsBuiltInExprCategory read FCategory write FCategory;
end;
{ TsExprIdentifierDefs }
TsExprIdentifierDefs = class(TCollection)
private
FParser: TsExpressionParser;
function GetI(AIndex: Integer): TsExprIdentifierDef;
procedure SetI(AIndex: Integer; const AValue: TsExprIdentifierDef);
protected
procedure Update(Item: TCollectionItem); override;
property Parser: TsExpressionParser read FParser;
public
function IndexOfIdentifier(const AName: ShortString): Integer;
function FindIdentifier(const AName: ShortString): TsExprIdentifierDef;
function IdentifierByName(const AName: ShortString): TsExprIdentifierDef;
function AddVariable(const AName: ShortString; AResultType: TsResultType;
AValue: String): TsExprIdentifierDef;
function AddBooleanVariable(const AName: ShortString;
AValue: Boolean): TsExprIdentifierDef;
function AddIntegerVariable(const AName: ShortString;
AValue: Integer): TsExprIdentifierDef;
function AddFloatVariable(const AName: ShortString;
AValue: TExprFloat): TsExprIdentifierDef;
function AddStringVariable(const AName: ShortString;
AValue: String): TsExprIdentifierDef;
function AddDateTimeVariable(const AName: ShortString;
AValue: TDateTime): TsExprIdentifierDef;
function AddFunction(const AName: ShortString; const AResultType: Char;
const AParamTypes: String; ACallBack: TsExprFunctionCallBack): TsExprIdentifierDef;
function AddFunction(const AName: ShortString; const AResultType: Char;
const AParamTypes: String; ACallBack: TsExprFunctionEvent): TsExprIdentifierDef;
property Identifiers[AIndex: Integer]: TsExprIdentifierDef read GetI write SetI; default;
end;
{ TsIdentifierExprNode }
TsIdentifierExprNode = class(TsExprNode)
private
FID: TsExprIdentifierDef;
PResult: PsExpressionResult;
FResultType: TsResultType;
public
constructor CreateIdentifier(AID: TsExprIdentifierDef);
function NodeType: TsResultType; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
property Identifier: TsExprIdentifierDef read FID;
end;
{ TFPExprVariable }
TFPExprVariable = class(TsIdentifierExprNode)
procedure Check; override;
function AsString: string; override;
Function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
end;
{ TFPExprFunction }
TFPExprFunction = class(TsIdentifierExprNode)
private
FArgumentNodes: TExprArgumentArray;
FargumentParams: TExprParameterArray;
protected
procedure CalcParams;
procedure Check; override;
public
constructor CreateFunction(AID: TsExprIdentifierDef;
const Args: TExprArgumentArray); virtual;
destructor Destroy; override;
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
property ArgumentNodes: TExprArgumentArray read FArgumentNodes;
property ArgumentParams: TExprParameterArray read FArgumentParams;
end;
{ TFPFunctionCallBack }
TFPFunctionCallBack = class(TFPExprFunction)
private
FCallBack: TsExprFunctionCallBack;
public
constructor CreateFunction(AID: TsExprIdentifierDef;
const Args: TExprArgumentArray); override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
property CallBack: TsExprFunctionCallBack read FCallBack;
end;
{ TFPFunctionEventHandler }
TFPFunctionEventHandler = class(TFPExprFunction)
private
FCallBack: TsExprFunctionEvent;
public
constructor CreateFunction(AID: TsExprIdentifierDef;
const Args: TExprArgumentArray); override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
property CallBack: TsExprFunctionEvent read FCallBack;
end;
{ TsCellExprNode }
TsCellExprNode = class(TsExprNode)
private
FWorksheet: TsWorksheet;
FCell: PCell;
FFlags: TsRelFlags;
public
constructor Create(AWorksheet: TsWorksheet; ACellString: String); overload;
constructor Create(AWorksheet: TsWorksheet; ACell: PCell; AFlags: TsRelFlags); overload;
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
procedure Check; override;
function NodeType: TsResultType; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsExpressionParser }
TsExpressionParser = class
private
FBuiltIns: TsBuiltInExprCategories;
FExpression: String;
FScanner: TsExpressionScanner;
FExprNode: TsExprNode;
FIdentifiers: TsExprIdentifierDefs;
FHashList: TFPHashObjectlist;
FDirty: Boolean;
FWorksheet: TsWorksheet;
procedure CheckEOF;
function ConvertNode(Todo: TsExprNode; ToType: TsResultType): TsExprNode;
function GetAsBoolean: Boolean;
function GetAsDateTime: TDateTime;
function GetAsFloat: TExprFloat;
function GetAsInteger: Int64;
function GetAsString: String;
function MatchNodes(Todo, Match: TsExprNode): TsExprNode;
procedure CheckNodes(var ALeft, ARight: TsExprNode);
procedure SetBuiltIns(const AValue: TsBuiltInExprCategories);
procedure SetIdentifiers(const AValue: TsExprIdentifierDefs);
protected
procedure ParserError(Msg: String);
procedure SetExpression(const AValue: String); virtual;
procedure CheckResultType(const Res: TsExpressionResult;
AType: TsResultType); inline;
class function BuiltinExpressionManager: TsBuiltInExpressionManager;
function Level1: TsExprNode;
function Level2: TsExprNode;
function Level3: TsExprNode;
function Level4: TsExprNode;
function Level5: TsExprNode;
function Level6: TsExprNode;
function Primitive: TsExprNode;
function GetToken: TTokenType;
function TokenType: TTokenType;
function CurrentToken: String;
procedure CreateHashList;
property Scanner: TsExpressionScanner read FScanner;
property ExprNode: TsExprNode read FExprNode;
property Dirty: Boolean read FDirty;
public
constructor Create(AWorksheet: TsWorksheet);
destructor Destroy; override;
function IdentifierByName(AName: ShortString): TsExprIdentifierDef; virtual;
procedure Clear;
function BuildRPNFormula: TsRPNFormula;
function BuildFormula: String;
procedure EvaluateExpression(var Result: TsExpressionResult);
function Evaluate: TsExpressionResult;
function ResultType: TsResultType;
property AsFloat: TExprFloat read GetAsFloat;
property AsInteger: Int64 read GetAsInteger;
property AsString: String read GetAsString;
property AsBoolean: Boolean read GetAsBoolean;
property AsDateTime: TDateTime read GetAsDateTime;
property Worksheet: TsWorksheet read FWorksheet;
// The expression to parse
property Expression: String read FExpression write SetExpression;
property Identifiers: TsExprIdentifierDefs read FIdentifiers write SetIdentifiers;
property BuiltIns: TsBuiltInExprCategories read FBuiltIns write SetBuiltIns;
end;
{ TsBuiltInExpressionManager }
TsBuiltInExpressionManager = class(TComponent)
private
FDefs: TsExprIdentifierDefs;
function GetCount: Integer;
function GetI(AIndex: Integer): TsBuiltInExprIdentifierDef;
protected
property Defs: TsExprIdentifierDefs read FDefs;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function IndexOfIdentifier(const AName: ShortString): Integer;
function FindIdentifier(const AName: ShortString): TsBuiltInExprIdentifierDef;
function IdentifierByName(const AName: ShortString): TsBuiltInExprIdentifierDef;
function AddVariable(const ACategory: TsBuiltInExprCategory; const AName: ShortString;
AResultType: TsResultType; AValue: String): TsBuiltInExprIdentifierDef;
function AddBooleanVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AValue: Boolean): TsBuiltInExprIdentifierDef;
function AddIntegerVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AValue: Integer): TsBuiltInExprIdentifierDef;
function AddFloatVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AValue: TExprFloat): TsBuiltInExprIdentifierDef;
function AddStringVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AValue: String): TsBuiltInExprIdentifierDef;
function AddDateTimeVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AValue: TDateTime): TsBuiltInExprIdentifierDef;
function AddFunction(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; const AResultType: Char; const AParamTypes: String;
ACallBack: TsExprFunctionCallBack): TsBuiltInExprIdentifierDef;
function AddFunction(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; const AResultType: Char; const AParamTypes: String;
ACallBack: TsExprFunctionEvent): TsBuiltInExprIdentifierDef;
property IdentifierCount: Integer read GetCount;
property Identifiers[AIndex: Integer]: TsBuiltInExprIdentifierDef read GetI;
end;
EExprParser = class(Exception);
function TokenName(AToken: TTokenType): String;
function ResultTypeName(AResult: TsResultType): String;
function CharToResultType(C: Char): TsResultType;
function BuiltinIdentifiers: TsBuiltInExpressionManager;
procedure RegisterStdBuiltins(AManager: TsBuiltInExpressionManager);
function ArgToFloat(Arg: TsExpressionResult): TExprFloat;
const
AllBuiltIns = [bcStrings, bcDateTime, bcMath, bcBoolean, bcConversion,
bcData, bcVaria, bcUser];
implementation
uses
typinfo, fpsutils;
const
cNull = #0;
cDoubleQuote = '"';
Digits = ['0'..'9', '.'];
WhiteSpace = [' ', #13, #10, #9];
Operators = ['+', '-', '<', '>', '=', '/', '*', '&', '%'];
Delimiters = Operators + [',', '(', ')'];
Symbols = ['^'] + Delimiters;
WordDelimiters = WhiteSpace + Symbols;
resourcestring
SBadQuotes = 'Unterminated string';
SUnknownDelimiter = 'Unknown delimiter character: "%s"';
SErrUnknownCharacter = 'Unknown character at pos %d: "%s"';
SErrUnexpectedEndOfExpression = 'Unexpected end of expression';
SErrUnknownComparison = 'Internal error: Unknown comparison';
SErrUnknownBooleanOp = 'Internal error: Unknown boolean operation';
SErrBracketExpected = 'Expected ) bracket at position %d, but got %s';
SerrUnknownTokenAtPos = 'Unknown token at pos %d : %s';
SErrLeftBracketExpected = 'Expected ( bracket at position %d, but got %s';
SErrInvalidFloat = '%s is not a valid floating-point value';
SErrUnknownIdentifier = 'Unknown identifier: %s';
SErrInExpression = 'Cannot evaluate: error in expression';
SErrInExpressionEmpty = 'Cannot evaluate: empty expression';
SErrCommaExpected = 'Expected comma (,) at position %d, but got %s';
SErrInvalidNumberChar = 'Unexpected character in number : %s';
SErrInvalidNumber = 'Invalid numerical value : %s';
SErrNoOperand = 'No operand for unary operation %s';
SErrNoLeftOperand = 'No left operand for binary operation %s';
SErrNoRightOperand = 'No left operand for binary operation %s';
SErrNoNegation = 'Cannot negate expression of type %s: %s';
SErrNoNOTOperation = 'Cannot perform NOT operation on expression of type %s: %s';
SErrNoPercentOperation = 'Cannot perform percent operation on expression of type %s: %s';
SErrNoXOROperationRPN = 'Cannot create RPN item for "xor" expression';
SErrTypesDoNotMatch = 'Type mismatch: %s<>%s for expressions "%s" and "%s".';
SErrTypesIncompatible = 'Incompatible types: %s<>%s for expressions "%s" and "%s".';
SErrNoNodeToCheck = 'Internal error: No node to check !';
SInvalidNodeType = 'Node type (%s) not in allowed types (%s) for expression: %s';
SErrUnterminatedExpression = 'Badly terminated expression. Found token at position %d : %s';
SErrDuplicateIdentifier = 'An identifier with name "%s" already exists.';
SErrInvalidResultCharacter = '"%s" is not a valid return type indicator';
ErrInvalidArgumentCount = 'Invalid argument count for function %s';
SErrInvalidArgumentType = 'Invalid type for argument %d: Expected %s, got %s';
SErrInvalidResultType = 'Invalid result type: %s';
SErrNotVariable = 'Identifier %s is not a variable';
SErrInactive = 'Operation not allowed while an expression is active';
SErrIFNeedsBoolean = 'First argument to IF must be of type boolean: %s';
SErrCaseLabelNotAConst = 'Case label %d "%s" is not a constant expression';
SErrCaseLabelType = 'Case label %d "%s" needs type %s, but has type %s';
SErrCaseValueType = 'Case value %d "%s" needs type %s, but has type %s';
SErrNoCellOperand = 'Cell operand is NIL.';
SErrCellError = 'Cell %s contains an error.';
{ ---------------------------------------------------------------------
Auxiliary functions
---------------------------------------------------------------------}
procedure RaiseParserError(Msg: String);
begin
raise EExprParser.Create(Msg);
end;
procedure RaiseParserError(Fmt: String; Args: Array of const);
begin
raise EExprParser.CreateFmt(Fmt, Args);
end;
function TokenName (AToken: TTokenType): String;
begin
Result := GetEnumName(TypeInfo(TTokenType), ord(AToken));
end;
function ResultTypeName(AResult: TsResultType): String;
begin
Result := GetEnumName(TypeInfo(TsResultType), ord(AResult));
end;
function CharToResultType(C: Char): TsResultType;
begin
case Upcase(C) of
'S' : Result := rtString;
'D' : Result := rtDateTime;
'B' : Result := rtBoolean;
'I' : Result := rtInteger;
'F' : Result := rtFloat;
else
RaiseParserError(SErrInvalidResultCharacter, [C]);
end;
end;
var
BuiltIns: TsBuiltInExpressionManager = nil;
function BuiltinIdentifiers: TsBuiltInExpressionManager;
begin
If (BuiltIns = nil) then
BuiltIns := TsBuiltInExpressionManager.Create(nil);
Result := BuiltIns;
end;
procedure FreeBuiltIns;
begin
FreeAndNil(Builtins);
end;
{------------------------------------------------------------------------------}
{ TsExpressionScanner }
{------------------------------------------------------------------------------}
constructor TsExpressionScanner.Create;
begin
Source := '';
end;
function TsExpressionScanner.IsAlpha(C: Char): Boolean;
begin
Result := C in ['A'..'Z', 'a'..'z'];
end;
procedure TsExpressionScanner.SetSource(const AValue: String);
begin
FSource := AValue;
LSource := Length(FSource);
FTokenType := ttEOF;
if LSource = 0 then
FPos := 0
else
FPos := 1;
FChar := PChar(FSource);
FToken := '';
end;
function TsExpressionScanner.NextPos: Char;
begin
Inc(FPos);
Inc(FChar);
Result := FChar^;
end;
function TsExpressionScanner.IsWordDelim(C: Char): Boolean;
begin
Result := C in WordDelimiters;
end;
function TsExpressionScanner.IsDelim(C: Char): Boolean;
begin
Result := C in Delimiters;
end;
function TsExpressionScanner.IsDigit(C: Char): Boolean;
begin
Result := C in Digits;
end;
procedure TsExpressionScanner.SkipWhiteSpace;
begin
while (FChar^ in WhiteSpace) and (FPos <= LSource) do
NextPos;
end;
function TsExpressionScanner.DoDelimiter: TTokenType;
var
B : Boolean;
C, D : Char;
begin
C := FChar^;
FToken := C;
B := C in ['<', '>'];
D := C;
C := NextPos;
if B and (C in ['=', '>']) then
begin
FToken := FToken + C;
NextPos;
If D = '>' then
Result := ttLargerThanEqual
else if C = '>' then
Result := ttNotEqual
else
Result := ttLessThanEqual;
end
else
case D of
'+' : Result := ttPlus;
'-' : Result := ttMinus;
'*' : Result := ttMul;
'/' : Result := ttDiv;
'^' : Result := ttPower;
'%' : Result := ttPercent;
'&' : Result := ttConcat;
'<' : Result := ttLessThan;
'>' : Result := ttLargerThan;
'=' : Result := ttEqual;
'(' : Result := ttLeft;
')' : Result := ttRight;
',' : Result := ttComma;
else
ScanError(Format(SUnknownDelimiter, [D]));
end;
end;
procedure TsExpressionScanner.ScanError(Msg: String);
begin
raise EExprScanner.Create(Msg)
end;
function TsExpressionScanner.DoString: TTokenType;
function TerminatingChar(C: Char): boolean;
begin
Result := (C = cNull)
or ((C = cDoubleQuote) and
not ((FPos < LSource) and (FSource[FPos+1] = cDoubleQuote)));
end;
var
C: Char;
begin
FToken := '';
C := NextPos;
while not TerminatingChar(C) do
begin
FToken := FToken+C;
if C = cDoubleQuote then
NextPos;
C := NextPos;
end;
if (C = cNull) then
ScanError(SBadQuotes);
Result := ttString;
FTokenType := Result;
NextPos;
end;
function TsExpressionScanner.GetCurrentChar: Char;
begin
if FChar <> nil then
Result := FChar^
else
Result := #0;
end;
function TsExpressionScanner.DoNumber: TTokenType;
var
C: Char;
X: TExprFloat;
I: Integer;
prevC: Char;
begin
C := CurrentChar;
prevC := #0;
while (not IsWordDelim(C) or (prevC = 'E')) and (C <> cNull) do
begin
if not ( IsDigit(C)
or ((FToken <> '') and (Upcase(C) = 'E'))
or ((FToken <> '') and (C in ['+', '-']) and (prevC = 'E'))
)
then
ScanError(Format(SErrInvalidNumberChar, [C]));
FToken := FToken+C;
prevC := Upcase(C);
C := NextPos;
end;
val(FToken, X, I);
if (I <> 0) then
ScanError(Format(SErrInvalidNumber, [FToken]));
Result := ttNumber;
end;
function TsExpressionScanner.DoIdentifier: TTokenType;
var
C: Char;
S: String;
row, row2: Cardinal;
col, col2: Cardinal;
flags: TsRelFlags;
begin
C := CurrentChar;
while (not IsWordDelim(C)) and (C <> cNull) do
begin
FToken := FToken + C;
C := NextPos;
end;
S := LowerCase(Token);
if (S = 'or') then
Result := ttOr
else if (S = 'xor') then
Result := ttXOr
else if (S = 'and') then
Result := ttAnd
else if (S = 'true') then
Result := ttTrue
else if (S = 'false') then
Result := ttFalse
else if (S = 'not') then
Result := ttNot
else if (S = 'if') then
Result := ttIF
else if ParseCellString(S, row, col, flags) then
Result := ttCell
else if ParseCellRangeString(S, row, col, row2, col2, flags) then
Result := ttCellRange
else
Result := ttIdentifier;
end;
function TsExpressionScanner.GetToken: TTokenType;
var
C: Char;
begin
FToken := '';
SkipWhiteSpace;
C := FChar^;
if c = cNull then
Result := ttEOF
else if IsDelim(C) then
Result := DoDelimiter
else if (C = cDoubleQuote) then
Result := DoString
else if IsDigit(C) then
Result := DoNumber
else if IsAlpha(C) then
Result := DoIdentifier
else
ScanError(Format(SErrUnknownCharacter, [FPos, C]));
FTokenType := Result;
end;
{------------------------------------------------------------------------------}
{ TsExpressionParser }
{------------------------------------------------------------------------------}
constructor TsExpressionParser.Create(AWorksheet: TsWorksheet);
begin
inherited Create;
FWorksheet := AWorksheet;
FIdentifiers := TsExprIdentifierDefs.Create(TsExprIdentifierDef);
FIdentifiers.FParser := Self;
FScanner := TsExpressionScanner.Create;
FHashList := TFPHashObjectList.Create(False);
end;
destructor TsExpressionParser.Destroy;
begin
FreeAndNil(FHashList);
FreeAndNil(FExprNode);
FreeAndNil(FIdentifiers);
FreeAndNil(FScanner);
inherited Destroy;
end;
function TsExpressionParser.BuildRPNFormula: TsRPNFormula;
begin
Result := CreateRPNFormula(FExprNode.AsRPNItem(nil), true);
end;
function TsExpressionParser.BuildFormula: String;
begin
Result := FExprNode.AsString;
end;
procedure TsExpressionParser.Clear;
begin
FExpression := '';
FHashList.Clear;
FExprNode.Free;
end;
procedure TsExpressionParser.CreateHashList;
var
ID: TsExprIdentifierDef;
BID: TsBuiltInExprIdentifierDef;
i: Integer;
M: TsBuiltInExpressionManager;
begin
FHashList.Clear;
// Builtins
M := BuiltinExpressionManager;
If (FBuiltins <> []) and Assigned(M) then
for i:=0 to M.IdentifierCount-1 do
begin
BID := M.Identifiers[i];
If BID.Category in FBuiltins then
FHashList.Add(LowerCase(BID.Name), BID);
end;
// User
for i:=0 to FIdentifiers.Count-1 do
begin
ID := FIdentifiers[i];
FHashList.Add(LowerCase(ID.Name), ID);
end;
FDirty := False;
end;
function TsExpressionParser.CurrentToken: String;
begin
Result := FScanner.Token;
end;
function TsExpressionParser.TokenType: TTokenType;
begin
Result := FScanner.TokenType;
end;
function TsExpressionParser.IdentifierByName(AName: ShortString): TsExprIdentifierDef;
begin
If FDirty then
CreateHashList;
Result := TsExprIdentifierDef(FHashList.Find(LowerCase(AName)));
end;
function TsExpressionParser.GetToken: TTokenType;
begin
Result := FScanner.GetToken;
end;
procedure TsExpressionParser.CheckEOF;
begin
if (TokenType = ttEOF) then
ParserError(SErrUnexpectedEndOfExpression);
end;
procedure TsExpressionParser.SetIdentifiers(const AValue: TsExprIdentifierDefs);
begin
FIdentifiers.Assign(AValue)
end;
procedure TsExpressionParser.EvaluateExpression(var Result: TsExpressionResult);
begin
if (FExpression = '') then
ParserError(SErrInExpressionEmpty);
if not Assigned(FExprNode) then
ParserError(SErrInExpression);
FExprNode.GetNodeValue(Result);
end;
procedure TsExpressionParser.ParserError(Msg: String);
begin
raise EExprParser.Create(Msg);
end;
function TsExpressionParser.ConvertNode(ToDo: TsExprNode;
ToType: TsResultType): TsExprNode;
begin
Result := ToDo;
case ToDo.NodeType of
rtInteger :
case ToType of
rtFloat : Result := TsIntToFloatExprNode.Create(Result);
rtDateTime : Result := TsIntToDateTimeExprNode.Create(Result);
end;
rtFloat :
case ToType of
rtDateTime : Result := TsFloatToDateTimeExprNode.Create(Result);
end;
end;
end;
function TsExpressionParser.GetAsBoolean: Boolean;
var
Res: TsExpressionResult;
begin
EvaluateExpression(Res);
CheckResultType(Res, rtBoolean);
Result := Res.ResBoolean;
end;
function TsExpressionParser.GetAsDateTime: TDateTime;
var
Res: TsExpressionResult;
begin
EvaluateExpression(Res);
CheckResultType(Res, rtDateTime);
Result := Res.ResDatetime;
end;
function TsExpressionParser.GetAsFloat: TExprFloat;
var
Res: TsExpressionResult;
begin
EvaluateExpression(Res);
CheckResultType(Res, rtFloat);
Result := Res.ResFloat;
end;
function TsExpressionParser.GetAsInteger: Int64;
var
Res: TsExpressionResult;
begin
EvaluateExpression(Res);
CheckResultType(Res, rtInteger);
Result := Res.ResInteger;
end;
function TsExpressionParser.GetAsString: String;
var
Res: TsExpressionResult;
begin
EvaluateExpression(Res);
CheckResultType(Res, rtString);
Result := Res.ResString;
end;
{
Checks types of todo and match. If ToDO can be converted to it matches
the type of match, then a node is inserted.
For binary operations, this function is called for both operands.
}
function TsExpressionParser.MatchNodes(ToDo, Match: TsExprNode): TsExprNode;
Var
TT, MT : TsResultType;
begin
Result := ToDo;
TT := ToDo.NodeType;
MT := Match.NodeType;
If TT <> MT then
begin
if TT = rtInteger then
begin
if (MT in [rtFloat, rtDateTime]) then
Result := ConvertNode(ToDo, MT);
end
else if (TT = rtFloat) then
begin
if (MT = rtDateTime) then
Result := ConvertNode(ToDo, rtDateTime);
end;
end;
end;
{
If the result types differ, they are converted to a common type if possible.
}
procedure TsExpressionParser.CheckNodes(var ALeft, ARight: TsExprNode);
begin
ALeft := MatchNodes(ALeft, ARight);
ARight := MatchNodes(ARight, ALeft);
end;
procedure TsExpressionParser.SetBuiltIns(const AValue: TsBuiltInExprCategories);
begin
if FBuiltIns = AValue then
exit;
FBuiltIns := AValue;
FDirty := true;
end;
function TsExpressionParser.Level1: TsExprNode;
var
tt: TTokenType;
Right: TsExprNode;
begin
{$ifdef debugexpr}Writeln('Level 1 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
if TokenType = ttNot then
begin
GetToken;
CheckEOF;
Right := Level2;
Result := TsNotExprNode.Create(Right);
end
else
Result := Level2;
try
while (TokenType in [ttAnd, ttOr, ttXor]) do
begin
tt := TokenType;
GetToken;
CheckEOF;
Right := Level2;
case tt of
ttOr : Result := TsBinaryOrExprNode.Create(Result, Right);
ttAnd : Result := TsBinaryAndExprNode.Create(Result, Right);
ttXor : Result := TsBinaryXorExprNode.Create(Result, Right);
else
ParserError(SErrUnknownBooleanOp)
end;
end;
except
Result.Free;
raise;
end;
end;
function TsExpressionParser.Level2: TsExprNode;
var
right: TsExprNode;
tt: TTokenType;
C: TsBinaryOperationExprNodeClass;
begin
{$ifdef debugexpr} Writeln('Level 2 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
Result := Level3;
try
if (TokenType in ttComparisons) then
begin
tt := TokenType;
GetToken;
CheckEOF;
Right := Level3;
CheckNodes(Result, right);
case tt of
ttLessthan : C := TsLessThanExprNode;
ttLessthanEqual : C := TsLessEqualExprNode;
ttLargerThan : C := TsGreaterThanExprNode;
ttLargerThanEqual : C := TsGreaterEqualExprNode;
ttEqual : C := TsEqualExprNode;
ttNotEqual : C := TsNotEqualExprNode;
else
ParserError(SErrUnknownComparison)
end;
Result := C.Create(Result, right);
end;
except
Result.Free;
raise;
end;
end;
function TsExpressionParser.Level3: TsExprNode;
var
tt: TTokenType;
right: TsExprNode;
begin
{$ifdef debugexpr} Writeln('Level 3 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
Result := Level4;
try
while TokenType in [ttPlus, ttMinus, ttConcat] do begin
tt := TokenType;
GetToken;
CheckEOF;
right := Level4;
CheckNodes(Result, right);
case tt of
ttPlus : Result := TsAddExprNode.Create(Result, right);
ttMinus : Result := TsSubtractExprNode.Create(Result, right);
ttConcat: Result := TsConcatExprNode.Create(Result, right);
end;
end;
except
Result.Free;
raise;
end;
end;
function TsExpressionParser.Level4: TsExprNode;
var
tt: TTokenType;
right: TsExprNode;
begin
{$ifdef debugexpr} Writeln('Level 4 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
Result := Level5;
try
while (TokenType in [ttMul, ttDiv]) do
begin
tt := TokenType;
GetToken;
right := Level5;
CheckNodes(Result, right);
case tt of
ttMul : Result := TsMultiplyExprNode.Create(Result, right);
ttDiv : Result := TsDivideExprNode.Create(Result, right);
end;
end;
except
Result.Free;
Raise;
end;
end;
function TsExpressionParser.Level5: TsExprNode;
var
B: Boolean;
begin
{$ifdef debugexpr} Writeln('Level 5 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
B := false;
if (TokenType in [ttPlus, ttMinus]) then
begin
B := (TokenType = ttMinus);
GetToken;
end;
Result := Level6;
if B then
Result := TsNegateExprNode.Create(Result);
end;
function TsExpressionParser.Level6: TsExprNode;
begin
{$ifdef debugexpr} Writeln('Level 6 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
if (TokenType = ttLeft) then
begin
GetToken;
Result := TsParenthesisExprNode.Create(Level1);
try
if (TokenType <> ttRight) then
ParserError(Format(SErrBracketExpected, [SCanner.Pos, CurrentToken]));
GetToken;
except
Result.Free;
raise;
end;
end
else
Result := Primitive;
end;
function TsExpressionParser.Primitive: TsExprNode;
var
I: Int64;
C: Integer;
X: TExprFloat;
ACount: Integer;
isIF: Boolean;
ID: TsExprIdentifierDef;
Args: TExprArgumentArray;
AI: Integer;
cell: PCell;
begin
{$ifdef debugexpr} Writeln('Primitive : ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
SetLength(Args, 0);
if (TokenType = ttNumber) then
begin
if TryStrToInt64(CurrentToken, I) then
Result := TsConstExprNode.CreateInteger(I)
else
begin
val(CurrentToken, X, C);
if (I = 0) then
Result := TsConstExprNode.CreateFloat(X)
else
ParserError(Format(SErrInvalidFloat, [CurrentToken]));
end;
end
else if (TokenType = ttString) then
Result := TsConstExprNode.CreateString(CurrentToken)
else if (TokenType in [ttTrue, ttFalse]) then
Result := TsConstExprNode.CreateBoolean(TokenType = ttTrue)
else if (TokenType = ttCell) then
Result := TsCellExprNode.Create(FWorksheet, CurrentToken)
else if (TokenType = ttCellRange) then
raise Exception.Create('Cell range missing')
else if not (TokenType in [ttIdentifier, ttIf]) then
ParserError(Format(SerrUnknownTokenAtPos, [Scanner.Pos, CurrentToken]))
else
begin
isIF := (TokenType = ttIf);
if not isIF then
begin
ID := self.IdentifierByName(CurrentToken);
If (ID = nil) then
ParserError(Format(SErrUnknownIdentifier,[CurrentToken]))
end;
// Determine number of arguments
if isIF then
ACount := 3
else if (ID.IdentifierType in [itFunctionCallBack, itFunctionHandler]) then
ACount := ID.ArgumentCount
else
ACount := 0;
// Parse arguments.
// Negative is for variable number of arguments, where Abs(value) is the minimum number of arguments
if (ACount <> 0) then
begin
GetToken;
if (TokenType <> ttLeft) then
ParserError(Format(SErrLeftBracketExpected, [Scanner.Pos, CurrentToken]));
SetLength(Args, abs(ACount));
AI := 0;
try
repeat
GetToken;
// Check if we must enlarge the argument array
if (ACount < 0) and (AI = Length(Args)) then
begin
SetLength(Args, AI+1);
Args[AI] := nil;
end;
Args[AI] := Level1;
inc(AI);
if (TokenType <> ttComma) then
if (AI < abs(ACount)) then
ParserError(Format(SErrCommaExpected, [Scanner.Pos, CurrentToken]))
until (AI = ACount) or ((ACount < 0) and (TokenType = ttRight));
if TokenType <> ttRight then
ParserError(Format(SErrBracketExpected, [Scanner.Pos, CurrentToken]));
except
on E: Exception do
begin
dec(AI);
while (AI >= 0) do
begin
FreeAndNil(Args[Ai]);
dec(AI);
end;
raise;
end;
end;
end;
if isIF then
Result := TIfExprNode.Create(Args[0], Args[1], Args[2])
else
case ID.IdentifierType of
itVariable : Result := TFPExprVariable.CreateIdentifier(ID);
itFunctionCallBack : Result := TFPFunctionCallback.CreateFunction(ID, Args);
itFunctionHandler : Result := TFPFunctionEventHandler.CreateFunction(ID, Args);
end;
end;
GetToken;
if TokenType = ttPercent then begin
Result := TsPercentExprNode.Create(Result);
GetToken;
end;
end;
procedure TsExpressionParser.SetExpression(const AValue: String);
begin
if FExpression = AValue then
exit;
FExpression := AValue;
FScanner.Source := AValue;
if Assigned(FExprNode) then
FreeAndNil(FExprNode);
if (FExpression <> '') then
begin
GetToken;
FExprNode := Level1;
if (TokenType <> ttEOF) then
ParserError(Format(SErrUnterminatedExpression, [Scanner.Pos, CurrentToken]));
FExprNode.Check;
end
else
FExprNode := nil;
end;
procedure TsExpressionParser.CheckResultType(const Res: TsExpressionResult;
AType: TsResultType); inline;
begin
if (Res.ResultType <> AType) then
RaiseParserError(SErrInvalidResultType, [ResultTypeName(Res.ResultType)]);
end;
class function TsExpressionParser.BuiltinExpressionManager: TsBuiltInExpressionManager;
begin
Result := BuiltinIdentifiers;
end;
function TsExpressionParser.Evaluate: TsExpressionResult;
begin
EvaluateExpression(Result);
end;
function TsExpressionParser.ResultType: TsResultType;
begin
if not Assigned(FExprNode) then
ParserError(SErrInExpression);
Result := FExprNode.NodeType;;
end;
{ ---------------------------------------------------------------------
TsExprIdentifierDefs
---------------------------------------------------------------------}
function TsExprIdentifierDefs.GetI(AIndex : Integer): TsExprIdentifierDef;
begin
Result := TsExprIdentifierDef(Items[AIndex]);
end;
procedure TsExprIdentifierDefs.SetI(AIndex: Integer;
const AValue: TsExprIdentifierDef);
begin
Items[AIndex] := AValue;
end;
procedure TsExprIdentifierDefs.Update(Item: TCollectionItem);
begin
if Assigned(FParser) then
FParser.FDirty := true;
end;
function TsExprIdentifierDefs.IndexOfIdentifier(const AName: ShortString): Integer;
begin
Result := Count-1;
while (Result >= 0) and (CompareText(GetI(Result).Name, AName) <> 0) do
dec(Result);
end;
function TsExprIdentifierDefs.FindIdentifier(const AName: ShortString
): TsExprIdentifierDef;
var
I: Integer;
begin
I := IndexOfIdentifier(AName);
if (I = -1) then
Result := nil
else
Result := GetI(I);
end;
function TsExprIdentifierDefs.IdentifierByName(const AName: ShortString
): TsExprIdentifierDef;
begin
Result := FindIdentifier(AName);
if (Result = nil) then
RaiseParserError(SErrUnknownIdentifier, [AName]);
end;
function TsExprIdentifierDefs.AddVariable(const AName: ShortString;
AResultType: TsResultType; AValue: String): TsExprIdentifierDef;
begin
Result := Add as TsExprIdentifierDef;
Result.IdentifierType := itVariable;
Result.Name := AName;
Result.ResultType := AResultType;
Result.Value := AValue;
end;
function TsExprIdentifierDefs.AddBooleanVariable(const AName: ShortString;
AValue: Boolean): TsExprIdentifierDef;
begin
Result := Add as TsExprIdentifierDef;
Result.IdentifierType := itVariable;
Result.Name := AName;
Result.ResultType := rtBoolean;
Result.FValue.ResBoolean := AValue;
end;
function TsExprIdentifierDefs.AddIntegerVariable(const AName: ShortString;
AValue: Integer): TsExprIdentifierDef;
begin
Result := Add as TsExprIdentifierDef;
Result.IdentifierType := itVariable;
Result.Name := AName;
Result.ResultType := rtInteger;
Result.FValue.ResInteger := AValue;
end;
function TsExprIdentifierDefs.AddFloatVariable(const AName: ShortString;
AValue: TExprFloat): TsExprIdentifierDef;
begin
Result := Add as TsExprIdentifierDef;
Result.IdentifierType := itVariable;
Result.Name := AName;
Result.ResultType := rtFloat;
Result.FValue.ResFloat := AValue;
end;
function TsExprIdentifierDefs.AddStringVariable(const AName: ShortString;
AValue: String): TsExprIdentifierDef;
begin
Result := Add as TsExprIdentifierDef;
Result.IdentifierType := itVariable;
Result.Name := AName;
Result.ResultType := rtString;
Result.FValue.ResString := AValue;
end;
function TsExprIdentifierDefs.AddDateTimeVariable(const AName: ShortString;
AValue: TDateTime): TsExprIdentifierDef;
begin
Result := Add as TsExprIdentifierDef;
Result.IdentifierType := itVariable;
Result.Name := AName;
Result.ResultType := rtDateTime;
Result.FValue.ResDateTime := AValue;
end;
function TsExprIdentifierDefs.AddFunction(const AName: ShortString;
const AResultType: Char; const AParamTypes: String;
ACallBack: TsExprFunctionCallBack): TsExprIdentifierDef;
begin
Result := Add as TsExprIdentifierDef;
Result.Name := AName;
Result.IdentifierType := itFunctionCallBack;
Result.ParameterTypes := AParamTypes;
Result.ResultType := CharToResultType(AResultType);
Result.FOnGetValueCB := ACallBack;
end;
function TsExprIdentifierDefs.AddFunction(const AName: ShortString;
const AResultType: Char; const AParamTypes: String;
ACallBack: TsExprFunctionEvent): TsExprIdentifierDef;
begin
Result := Add as TsExprIdentifierDef;
Result.Name := AName;
Result.IdentifierType := itFunctionHandler;
Result.ParameterTypes := AParamTypes;
Result.ResultType := CharToResultType(AResultType);
Result.FOnGetValue := ACallBack;
end;
{------------------------------------------------------------------------------}
{ TsExprIdentifierDef }
{------------------------------------------------------------------------------}
procedure TsExprIdentifierDef.SetName(const AValue: ShortString);
begin
if FName = AValue then
exit;
if (AValue <> '') then
if Assigned(Collection) and (TsExprIdentifierDefs(Collection).IndexOfIdentifier(AValue) <> -1) then
RaiseParserError(SErrDuplicateIdentifier,[AValue]);
FName := AValue;
end;
procedure TsExprIdentifierDef.SetResultType(const AValue: TsResultType);
begin
if AValue <> FValue.ResultType then
begin
FValue.ResultType := AValue;
SetValue(FStringValue);
end;
end;
procedure TsExprIdentifierDef.SetValue(const AValue: String);
begin
FStringValue := AValue;
if (AValue <> '') then
case FValue.ResultType of
rtBoolean : FValue.ResBoolean := FStringValue='True';
rtInteger : FValue.ResInteger := StrToInt(AValue);
rtFloat : FValue.ResFloat := StrToFloat(AValue);
rtDateTime : FValue.ResDateTime := StrToDateTime(AValue);
rtString : FValue.ResString := AValue;
end
else
case FValue.ResultType of
rtBoolean : FValue.ResBoolean := false;
rtInteger : FValue.ResInteger := 0;
rtFloat : FValue.ResFloat := 0.0;
rtDateTime : FValue.ResDateTime := 0;
rtString : FValue.ResString := '';
end
end;
procedure TsExprIdentifierDef.CheckResultType(const AType: TsResultType);
begin
if FValue.ResultType <> AType then
RaiseParserError(SErrInvalidResultType, [ResultTypeName(AType)])
end;
procedure TsExprIdentifierDef.CheckVariable;
begin
if Identifiertype <> itVariable then
RaiseParserError(SErrNotVariable, [Name]);
end;
function TsExprIdentifierDef.ArgumentCount: Integer;
begin
Result := Length(FArgumentTypes);
end;
procedure TsExprIdentifierDef.Assign(Source: TPersistent);
var
EID: TsExprIdentifierDef;
begin
if (Source is TsExprIdentifierDef) then
begin
EID := Source as TsExprIdentifierDef;
FStringValue := EID.FStringValue;
FValue := EID.FValue;
FArgumentTypes := EID.FArgumentTypes;
FIDType := EID.FIDType;
FName := EID.FName;
FOnGetValue := EID.FOnGetValue;
FOnGetValueCB := EID.FOnGetValueCB;
end
else
inherited Assign(Source);
end;
procedure TsExprIdentifierDef.SetArgumentTypes(const AValue: String);
var
i: integer;
begin
if FArgumentTypes = AValue then
exit;
for i:=1 to Length(AValue) do
CharToResultType(AValue[i]);
FArgumentTypes := AValue;
end;
procedure TsExprIdentifierDef.SetAsBoolean(const AValue: Boolean);
begin
CheckVariable;
CheckResultType(rtBoolean);
FValue.ResBoolean := AValue;
end;
procedure TsExprIdentifierDef.SetAsDateTime(const AValue: TDateTime);
begin
CheckVariable;
CheckResultType(rtDateTime);
FValue.ResDateTime := AValue;
end;
procedure TsExprIdentifierDef.SetAsFloat(const AValue: TExprFloat);
begin
CheckVariable;
CheckResultType(rtFloat);
FValue.ResFloat := AValue;
end;
procedure TsExprIdentifierDef.SetAsInteger(const AValue: Int64);
begin
CheckVariable;
CheckResultType(rtInteger);
FValue.ResInteger := AValue;
end;
procedure TsExprIdentifierDef.SetAsString(const AValue: String);
begin
CheckVariable;
CheckResultType(rtString);
FValue.resString := AValue;
end;
function TsExprIdentifierDef.GetValue: String;
begin
case FValue.ResultType of
rtBoolean : if FValue.ResBoolean then
Result := 'True'
else
Result := 'False';
rtInteger : Result := IntToStr(FValue.ResInteger);
rtFloat : Result := FloatToStr(FValue.ResFloat);
rtDateTime : Result := FormatDateTime('cccc', FValue.ResDateTime);
rtString : Result := FValue.ResString;
end;
end;
function TsExprIdentifierDef.GetResultType: TsResultType;
begin
Result := FValue.ResultType;
end;
function TsExprIdentifierDef.GetAsFloat: TExprFloat;
begin
CheckResultType(rtFloat);
CheckVariable;
Result := FValue.ResFloat;
end;
function TsExprIdentifierDef.GetAsBoolean: Boolean;
begin
CheckResultType(rtBoolean);
CheckVariable;
Result := FValue.ResBoolean;
end;
function TsExprIdentifierDef.GetAsDateTime: TDateTime;
begin
CheckResultType(rtDateTime);
CheckVariable;
Result := FValue.ResDateTime;
end;
function TsExprIdentifierDef.GetAsInteger: Int64;
begin
CheckResultType(rtInteger);
CheckVariable;
Result := FValue.ResInteger;
end;
function TsExprIdentifierDef.GetAsString: String;
begin
CheckResultType(rtString);
CheckVariable;
Result := FValue.ResString;
end;
{------------------------------------------------------------------------------}
{ TsBuiltInExpressionManager }
{------------------------------------------------------------------------------}
constructor TsBuiltInExpressionManager.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FDefs := TsExprIdentifierDefs.Create(TsBuiltInExprIdentifierDef)
end;
destructor TsBuiltInExpressionManager.Destroy;
begin
FreeAndNil(FDefs);
inherited Destroy;
end;
function TsBuiltInExpressionManager.GetCount: Integer;
begin
Result := FDefs.Count;
end;
function TsBuiltInExpressionManager.GetI(AIndex: Integer): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs[Aindex])
end;
function TsBuiltInExpressionManager.IndexOfIdentifier(const AName: ShortString): Integer;
begin
Result := FDefs.IndexOfIdentifier(AName);
end;
function TsBuiltInExpressionManager.FindIdentifier(const AName: ShortString
): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.FindIdentifier(AName));
end;
function TsBuiltInExpressionManager.IdentifierByName(const AName: ShortString
): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.IdentifierByName(AName));
end;
function TsBuiltInExpressionManager.AddVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AResultType: TsResultType; AValue: String
): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.Addvariable(AName, AResultType, AValue));
Result.Category := ACategory;
end;
function TsBuiltInExpressionManager.AddBooleanVariable(
const ACategory: TsBuiltInExprCategory; const AName: ShortString; AValue: Boolean
): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.AddBooleanvariable(AName, AValue));
Result.Category := ACategory;
end;
function TsBuiltInExpressionManager.AddIntegerVariable(
const ACategory: TsBuiltInExprCategory; const AName: ShortString; AValue: Integer
): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.AddIntegerVariable(AName, AValue));
Result.Category := ACategory;
end;
function TsBuiltInExpressionManager.AddFloatVariable(
const ACategory: TsBuiltInExprCategory; const AName: ShortString;
AValue: TExprFloat): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.AddFloatVariable(AName, AValue));
Result.Category := ACategory;
end;
function TsBuiltInExpressionManager.AddStringVariable(
const ACategory: TsBuiltInExprCategory; const AName: ShortString; AValue: String
): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.AddStringVariable(AName, AValue));
Result.Category := ACategory;
end;
function TsBuiltInExpressionManager.AddDateTimeVariable(
const ACategory: TsBuiltInExprCategory; const AName: ShortString; AValue: TDateTime
): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.AddDateTimeVariable(AName, AValue));
Result.Category := ACategory;
end;
function TsBuiltInExpressionManager.AddFunction(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; const AResultType: Char; const AParamTypes: String;
ACallBack: TsExprFunctionCallBack): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.AddFunction(AName, AResultType, AParamTypes, ACallBack));
Result.Category := ACategory;
end;
function TsBuiltInExpressionManager.AddFunction(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; const AResultType: Char; const AParamTypes: String;
ACallBack: TsExprFunctionEvent): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.AddFunction(AName, AResultType, AParamTypes, ACallBack));
Result.Category := ACategory;
end;
{------------------------------------------------------------------------------}
{ Various Nodes }
{------------------------------------------------------------------------------}
{ TsBinaryOperationExprNode }
constructor TsBinaryOperationExprNode.Create(ALeft, ARight: TsExprNode);
begin
FLeft := ALeft;
FRight := ARight;
end;
destructor TsBinaryOperationExprNode.Destroy;
begin
FreeAndNil(FLeft);
FreeAndNil(FRight);
inherited Destroy;
end;
procedure TsBinaryOperationExprNode.Check;
begin
if not Assigned(Left) then
RaiseParserError(SErrNoLeftOperand,[classname]);
if not Assigned(Right) then
RaiseParserError(SErrNoRightOperand,[classname]);
end;
procedure TsBinaryOperationExprNode.CheckSameNodeTypes;
var
LT, RT: TsResultType;
begin
LT := Left.NodeType;
RT := Right.NodeType;
if (RT <> LT) then
RaiseParserError(SErrTypesDoNotMatch, [ResultTypeName(LT), ResultTypeName(RT), Left.AsString, Right.AsString])
end;
{ TsUnaryOperationExprNode }
constructor TsUnaryOperationExprNode.Create(AOperand: TsExprNode);
begin
FOperand := AOperand;
end;
destructor TsUnaryOperationExprNode.Destroy;
begin
FreeAndNil(FOperand);
inherited Destroy;
end;
procedure TsUnaryOperationExprNode.Check;
begin
if not Assigned(Operand) then
RaiseParserError(SErrNoOperand, [Self.ClassName]);
end;
{ TsConstExprNode }
constructor TsConstExprNode.CreateString(AValue: String);
begin
FValue.ResultType := rtString;
FValue.ResString := AValue;
end;
constructor TsConstExprNode.CreateInteger(AValue: Int64);
begin
FValue.ResultType := rtInteger;
FValue.ResInteger := AValue;
end;
constructor TsConstExprNode.CreateDateTime(AValue: TDateTime);
begin
FValue.ResultType := rtDateTime;
FValue.ResDateTime := AValue;
end;
constructor TsConstExprNode.CreateFloat(AValue: TExprFloat);
begin
Inherited Create;
FValue.ResultType := rtFloat;
FValue.ResFloat := AValue;
end;
constructor TsConstExprNode.CreateBoolean(AValue: Boolean);
begin
FValue.ResultType := rtBoolean;
FValue.ResBoolean := AValue;
end;
procedure TsConstExprNode.Check;
begin
// Nothing to check;
end;
function TsConstExprNode.NodeType: TsResultType;
begin
Result := FValue.ResultType;
end;
procedure TsConstExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Result := FValue;
end;
function TsConstExprNode.AsString: string;
begin
case NodeType of
rtString : Result := cDoubleQuote + FValue.ResString + cDoubleQuote;
rtInteger : Result := IntToStr(FValue.ResInteger);
rtDateTime : Result := '''' + FormatDateTime('cccc', FValue.ResDateTime) + ''''; // Probably wrong !!!
rtBoolean : if FValue.ResBoolean then Result := 'TRUE' else Result := 'FALSE';
rtFloat : Str(FValue.ResFloat, Result);
end;
end;
function TsConstExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
case NodeType of
rtString : Result := RPNString(FValue.ResString, ANext);
rtInteger : Result := RPNNumber(FValue.ResInteger, ANext);
rtDateTime : Result := RPNNumber(FValue.ResDateTime, ANext);
rtBoolean : Result := RPNBool(FValue.ResBoolean, ANext);
rtFloat : Result := RPNNumber(FValue.ResFloat, ANext);
end;
end;
{ TsNegateExprNode }
function TsNegateExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekUMinus,
Operand.AsRPNItem(
ANext
));
end;
function TsNegateExprNode.AsString: String;
begin
Result := '-' + TrimLeft(Operand.AsString);
end;
procedure TsNegateExprNode.Check;
begin
inherited;
if not (Operand.NodeType in [rtInteger, rtFloat]) then
RaiseParserError(SErrNoNegation, [ResultTypeName(Operand.NodeType), Operand.AsString])
end;
procedure TsNegateExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetNodeValue(Result);
case Result.ResultType of
rtInteger : Result.ResInteger := -Result.ResInteger;
rtFloat : Result.ResFloat := -Result.ResFloat;
end;
end;
function TsNegateExprNode.NodeType: TsResultType;
begin
Result := Operand.NodeType;
end;
{ TsPercentExprNode }
function TsPercentExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekPercent,
Operand.AsRPNItem(
ANext
));
end;
function TsPercentExprNode.AsString: String;
begin
Result := Operand.AsString + '%';
end;
procedure TsPercentExprNode.Check;
begin
inherited;
if not (Operand.NodeType in [rtInteger, rtFloat]) then
RaiseParserError(SErrNoPercentOperation, [ResultTypeName(Operand.NodeType), Operand.AsString])
end;
procedure TsPercentExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetNodeValue(Result);
case Result.ResultType of
rtInteger : Result.ResFloat := 0.01 * Result.ResInteger;
rtFloat : Result.ResFloat := 0.01 * Result.ResFloat;
end;
Result.ResultType := Nodetype;
end;
function TsPercentExprNode.NodeType: TsResultType;
begin
Result := rtFloat;
end;
{ TsParenthesisExprNode }
function TsParenthesisExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekParen,
Operand.AsRPNItem(
ANext
));
end;
function TsParenthesisExprNode.AsString: String;
begin
Result := '(' + Operand.AsString + ')';
end;
function TsParenthesisExprNode.NodeType: TsResultType;
begin
Result := Operand.NodeType;
end;
procedure TsParenthesisExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Result := Operand.NodeValue;
end;
{ TsBinaryAndExprNode }
function TsBinaryAndExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekAND,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsBinaryAndExprNode.AsString: string;
begin
Result := Left.AsString + ' and ' + Right.AsString;
end;
procedure TsBooleanOperationExprNode.Check;
begin
inherited Check;
CheckNodeType(Left, [rtInteger, rtBoolean]);
CheckNodeType(Right, [rtInteger, rtBoolean]);
CheckSameNodeTypes;
end;
function TsBooleanOperationExprNode.NodeType: TsResultType;
begin
Result := Left.NodeType;
end;
procedure TsBinaryAndExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtBoolean : Result.resBoolean := Result.ResBoolean and RRes.ResBoolean;
rtInteger : Result.resInteger := Result.ResInteger and RRes.ResInteger;
end;
end;
{ TsExprNode }
procedure TsExprNode.CheckNodeType(Anode: TsExprNode; Allowed: TsResultTypes);
var
S: String;
A: TsResultType;
begin
if (Anode = nil) then
RaiseParserError(SErrNoNodeToCheck);
if not (ANode.NodeType in Allowed) then
begin
S := '';
for A := Low(TsResultType) to High(TsResultType) do
if A in Allowed then
begin
if S <> '' then
S := S + ',';
S := S + ResultTypeName(A);
end;
RaiseParserError(SInvalidNodeType, [ResultTypeName(ANode.NodeType), S, ANode.AsString]);
end;
end;
function TsExprNode.NodeValue: TsExpressionResult;
begin
GetNodeValue(Result);
end;
{ TsBinaryOrExprNode }
function TsBinaryOrExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekOR,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsBinaryOrExprNode.AsString: string;
begin
Result := Left.AsString + ' or ' + Right.AsString;
end;
procedure TsBinaryOrExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtBoolean : Result.resBoolean := Result.ResBoolean or RRes.ResBoolean;
rtInteger : Result.resInteger := Result.ResInteger or RRes.ResInteger;
end;
end;
{ TsBinaryXorExprNode }
function TsBinaryXorExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
RaiseParserError(SErrNoXOROperationRPN);
end;
function TsBinaryXorExprNode.AsString: string;
begin
Result := Left.AsString + ' xor ' + Right.AsString;
end;
procedure TsBinaryXorExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtBoolean : Result.resBoolean := Result.ResBoolean xor RRes.ResBoolean;
rtInteger : Result.resInteger := Result.ResInteger xor RRes.ResInteger;
end;
end;
{ TsNotExprNode }
function TsNotExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekNOT,
Operand.AsRPNItem(
ANext
));
end;
function TsNotExprNode.AsString: String;
begin
Result := 'not ' + Operand.AsString;
end;
procedure TsNotExprNode.Check;
begin
if not (Operand.NodeType in [rtInteger, rtBoolean]) then
RaiseParserError(SErrNoNotOperation, [ResultTypeName(Operand.NodeType), Operand.AsString])
end;
procedure TsNotExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetNodeValue(Result);
case Result.ResultType of
rtInteger : Result.ResInteger := not Result.ResInteger;
rtBoolean : Result.ResBoolean := not Result.ResBoolean;
end
end;
function TsNotExprNode.NodeType: TsResultType;
begin
Result := Operand.NodeType;
end;
{ TIfExprNode }
constructor TIfExprNode.Create(ACondition, ALeft, ARight: TsExprNode);
begin
inherited Create(ALeft,ARight);
FCondition := ACondition;
end;
destructor TIfExprNode.Destroy;
begin
FreeAndNil(FCondition);
inherited Destroy;
end;
function TIfExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
if Left = nil then
Result := RPNFunc(fekIF,
Right.AsRPNItem(
ANext
))
else
Result := RPNFunc(fekIF,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TIfExprNode.AsString: string;
begin
if Right = nil then
Result := Format('IF(%s, %s)', [Condition.AsString, Left.AsString])
else
Result := Format('IF(%s, %s, %s)',[Condition.AsString, Left.AsString, Right.AsString]);
end;
procedure TIfExprNode.Check;
begin
inherited Check;
if (Condition.NodeType <> rtBoolean) then
RaiseParserError(SErrIFNeedsBoolean, [Condition.AsString]);
CheckSameNodeTypes;
end;
procedure TIfExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
FCondition.GetNodeValue(Result);
if Result.ResBoolean then
Left.GetNodeValue(Result)
else
Right.GetNodeValue(Result)
end;
function TIfExprNode.NodeType: TsResultType;
begin
Result := Left.NodeType;
end;
{ TsBooleanResultExprNode }
procedure TsBooleanResultExprNode.Check;
begin
inherited Check;
CheckSameNodeTypes;
end;
function TsBooleanResultExprNode.NodeType: TsResultType;
begin
Result := rtBoolean;
end;
{ TsEqualExprNode }
function TsEqualExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekEqual,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsEqualExprNode.AsString: string;
begin
Result := Left.AsString + ' = ' + Right.AsString;
end;
procedure TsEqualExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtBoolean : Result.resBoolean := Result.ResBoolean = RRes.ResBoolean;
rtInteger : Result.resBoolean := Result.ResInteger = RRes.ResInteger;
rtFloat : Result.resBoolean := Result.ResFloat = RRes.ResFLoat;
rtDateTime : Result.resBoolean := Result.ResDateTime = RRes.ResDateTime;
rtString : Result.resBoolean := Result.ResString = RRes.ResString;
end;
Result.ResultType := rtBoolean;
end;
{ TsNotEqualExprNode }
function TsNotEqualExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekNotEqual,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsNotEqualExprNode.AsString: string;
begin
Result := Left.AsString + ' <> ' + Right.AsString;
end;
procedure TsNotEqualExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
inherited GetNodeValue(Result);
Result.ResBoolean := not Result.ResBoolean;
end;
{ TsLessThanExprNode }
function TsLessThanExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekLess,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsLessThanExprNode.AsString: string;
begin
Result := Left.AsString + ' < ' + Right.AsString;
end;
procedure TsLessThanExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtInteger : Result.resBoolean := Result.ResInteger < RRes.ResInteger;
rtFloat : Result.resBoolean := Result.ResFloat < RRes.ResFLoat;
rtDateTime : Result.resBoolean := Result.ResDateTime < RRes.ResDateTime;
rtString : Result.resBoolean := Result.ResString < RRes.ResString;
end;
Result.ResultType := rtBoolean;
end;
{ TsGreaterThanExprNode }
function TsGreaterThanExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekGreater,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsGreaterThanExprNode.AsString: string;
begin
Result := Left.AsString + ' > ' + Right.AsString;
end;
procedure TsGreaterThanExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtInteger : case Right.NodeType of
rtInteger : Result.resBoolean := Result.ResInteger > RRes.ResInteger;
rtFloat : Result.resBoolean := Result.ResInteger > RRes.ResFloat;
end;
rtFloat : case Right.NodeType of
rtInteger : Result.resBoolean := Result.ResFloat > RRes.ResInteger;
rtFloat : Result.resBoolean := Result.ResFloat > RRes.ResFLoat;
end;
rtDateTime : Result.resBoolean := Result.ResDateTime > RRes.ResDateTime;
rtString : Result.resBoolean := Result.ResString > RRes.ResString;
end;
Result.ResultType := rtBoolean;
end;
{ TsGreaterEqualExprNode }
function TsGreaterEqualExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekGreaterEqual,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsGreaterEqualExprNode.AsString: string;
begin
Result := Left.AsString + ' >= ' + Right.AsString;
end;
procedure TsGreaterEqualExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
inherited GetNodeValue(Result);
Result.ResBoolean := not Result.ResBoolean;
end;
{ TsLessEqualExprNode }
function TsLessEqualExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekLessEqual,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsLessEqualExprNode.AsString: string;
begin
Result := Left.AsString + ' <= ' + Right.AsString;
end;
procedure TsLessEqualExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
inherited GetNodeValue(Result);
Result.ResBoolean := not Result.ResBoolean;
end;
{ TsOrderingExprNode }
procedure TsOrderingExprNode.Check;
const
AllowedTypes =[rtInteger, rtfloat, rtDateTime, rtString];
begin
CheckNodeType(Left, AllowedTypes);
CheckNodeType(Right, AllowedTypes);
inherited Check;
end;
{ TsConcatExprNode }
procedure TsConcatExprNode.Check;
begin
inherited Check;
CheckNodeType(Left, [rtString]);
CheckNodeType(Right, [rtString]);
end;
procedure TsConcatExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes : TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
Result.ResString := Result.ResString + RRes.ResString;
Result.ResultType := rtString;
end;
function TsConcatExprNode.NodeType: TsResultType;
begin
Result := rtString;
end;
function TsConcatExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekConcat,
Right.AsRPNItem(
Left.AsRPNItem(
nil)));
end;
function TsConcatExprNode.AsString: string;
begin
Result := Left.AsString + '&' + Right.AsString;
end;
{ TsMathOperationExprNode }
procedure TsMathOperationExprNode.Check;
const
AllowedTypes = [rtInteger, rtfloat, rtDateTime];
begin
inherited Check;
CheckNodeType(Left, AllowedTypes);
CheckNodeType(Right, AllowedTypes);
CheckSameNodeTypes;
end;
function TsMathOperationExprNode.NodeType: TsResultType;
begin
Result := Left.NodeType;
end;
{ TsAddExprNode }
function TsAddExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekAdd,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsAddExprNode.AsString: string;
begin
Result := Left.AsString + '+' + Right.AsString;
end;
procedure TsAddExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes : TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtInteger : Result.ResInteger := Result.ResInteger + RRes.ResInteger;
// rtString : Result.ResString := Result.ResString + RRes.ResString;
rtDateTime : Result.ResDateTime := Result.ResDateTime + RRes.ResDateTime;
rtFloat : Result.ResFloat := Result.ResFloat + RRes.ResFloat;
end;
Result.ResultType := NodeType;
end;
{ TsSubtractExprNode }
function TsSubtractExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekSub,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsSubtractExprNode.AsString: string;
begin
Result := Left.AsString + '-' + Right.asString;
end;
procedure TsSubtractExprNode.Check;
const
AllowedTypes =[rtInteger, rtfloat, rtDateTime];
begin
CheckNodeType(Left, AllowedTypes);
CheckNodeType(Right, AllowedTypes);
inherited Check;
end;
procedure TsSubtractExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtInteger : Result.ResInteger := Result.ResInteger - RRes.ResInteger;
rtDateTime : Result.ResDateTime := Result.ResDateTime - RRes.ResDateTime;
rtFloat : Result.ResFLoat := Result.ResFLoat - RRes.ResFLoat;
end;
end;
{ TsMultiplyExprNode }
function TsMultiplyExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekMul,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsMultiplyExprNode.AsString: string;
begin
Result := Left.AsString + '*' + Right.AsString;
end;
procedure TsMultiplyExprNode.Check;
const
AllowedTypes = [rtInteger, rtFloat];
begin
CheckNodeType(Left, AllowedTypes);
CheckNodeType(Right, AllowedTypes);
inherited;
end;
procedure TsMultiplyExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtInteger : Result.ResInteger := Result.ResInteger * RRes.ResInteger;
rtFloat : Result.ResFloat := Result.ResFloat * RRes.ResFloat;
end;
end;
{ TsDivideExprNode }
function TsDivideExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekDiv,
Right.AsRPNItem(
Left.AsRPNItem(
ANext
)));
end;
function TsDivideExprNode.AsString: string;
begin
Result := Left.AsString + '/' + Right.asString;
end;
procedure TsDivideExprNode.Check;
const
AllowedTypes =[rtInteger, rtFloat];
begin
CheckNodeType(Left, AllowedTypes);
CheckNodeType(Right, AllowedTypes);
inherited Check;
end;
procedure TsDivideExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
case Result.ResultType of
rtInteger : Result.ResFloat := Result.ResInteger / RRes.ResInteger;
rtFloat : Result.ResFloat := Result.ResFloat / RRes.ResFloat;
end;
Result.ResultType := rtFloat;
end;
function TsDivideExprNode.NodeType: TsResultType;
begin
Result := rtFLoat;
end;
{ TsConvertExprNode }
function TsConvertExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := Operand.AsRPNItem(ANext);
end;
function TsConvertExprNode.AsString: String;
begin
Result := Operand.AsString;
end;
{ TsIntToFloatExprNode }
procedure TsConvertToIntExprNode.Check;
begin
inherited Check;
CheckNodeType(Operand, [rtInteger])
end;
procedure TsIntToFloatExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetNodeValue(Result);
Result.ResFloat := Result.ResInteger;
Result.ResultType := rtFloat;
end;
function TsIntToFloatExprNode.NodeType: TsResultType;
begin
Result := rtFloat;
end;
{ TsIntToDateTimeExprNode }
function TsIntToDateTimeExprNode.NodeType: TsResultType;
begin
Result := rtDatetime;
end;
procedure TsIntToDateTimeExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetnodeValue(Result);
Result.ResDateTime := Result.ResInteger;
Result.ResultType := rtDateTime;
end;
{ TsFloatToDateTimeExprNode }
procedure TsFloatToDateTimeExprNode.Check;
begin
inherited Check;
CheckNodeType(Operand, [rtFloat]);
end;
function TsFloatToDateTimeExprNode.NodeType: TsResultType;
begin
Result := rtDateTime;
end;
procedure TsFloatToDateTimeExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetNodeValue(Result);
Result.ResDateTime := Result.ResFloat;
Result.ResultType := rtDateTime;
end;
{ TsIdentifierExprNode }
constructor TsIdentifierExprNode.CreateIdentifier(AID: TsExprIdentifierDef);
begin
inherited Create;
FID := AID;
PResult := @FID.FValue;
FResultType := FID.ResultType;
end;
function TsIdentifierExprNode.NodeType: TsResultType;
begin
Result := FResultType;
end;
procedure TsIdentifierExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Result := PResult^;
Result.ResultType := FResultType;
end;
{ TFPExprVariable }
procedure TFPExprVariable.Check;
begin
// Do nothing;
end;
function TFPExprVariable.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
RaiseParserError('Cannot handle variables for RPN, so far.');
end;
function TFPExprVariable.AsString: string;
begin
Result := FID.Name;
end;
{ TFPExprFunction }
constructor TFPExprFunction.CreateFunction(AID: TsExprIdentifierDef;
const Args: TExprArgumentArray);
begin
inherited CreateIdentifier(AID);
FArgumentNodes := Args;
SetLength(FArgumentParams, Length(Args));
end;
destructor TFPExprFunction.Destroy;
var
i: Integer;
begin
for i:=0 to Length(FArgumentNodes)-1 do
FreeAndNil(FArgumentNodes[i]);
inherited Destroy;
end;
function TFPExprFunction.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := ANext;
// RaiseParserError('Cannot handle functions for RPN, so far.');
end;
function TFPExprFunction.AsString: String;
var
S : String;
i : Integer;
begin
S := '';
for i := 0 to Length(FArgumentNodes)-1 do
begin
if (S <> '') then
S := S + ',';
S := S + FArgumentNodes[i].AsString;
end;
if (S <> '') then
S := '(' + S + ')';
Result := FID.Name + S;
end;
procedure TFPExprFunction.CalcParams;
var
i : Integer;
begin
for i := 0 to Length(FArgumentParams)-1 do
FArgumentNodes[i].GetNodeValue(FArgumentParams[i]);
end;
procedure TFPExprFunction.Check;
var
i: Integer;
rtp, rta: TsResultType;
begin
if Length(FArgumentNodes) <> FID.ArgumentCount then
RaiseParserError(ErrInvalidArgumentCount, [FID.Name]);
for i := 0 to Length(FArgumentNodes)-1 do
begin
rtp := CharToResultType(FID.ParameterTypes[i+1]);
rta := FArgumentNodes[i].NodeType;
if (rtp <> rta) then
begin
// Automatically convert integers to floats in functions that return
// a float
if (rta = rtInteger) and (rtp = rtFloat) then
begin
FArgumentNodes[i] := TsIntToFloatExprNode(FArgumentNodes[i]);
exit;
end;
RaiseParserError(SErrInvalidArgumentType, [I+1, ResultTypeName(rtp), ResultTypeName(rta)])
end;
end;
end;
{ TFPFunctionCallBack }
constructor TFPFunctionCallBack.CreateFunction(AID: TsExprIdentifierDef;
const Args: TExprArgumentArray);
begin
inherited;
FCallBack := AID.OnGetFunctionValueCallBack;
end;
procedure TFPFunctionCallBack.GetNodeValue(var Result: TsExpressionResult);
begin
if Length(FArgumentParams) > 0 then
CalcParams;
FCallBack(Result, FArgumentParams);
Result.ResultType := NodeType;
end;
{ TFPFunctionEventHandler }
constructor TFPFunctionEventHandler.CreateFunction(AID: TsExprIdentifierDef;
const Args: TExprArgumentArray);
begin
inherited;
FCallBack := AID.OnGetFunctionValue;
end;
procedure TFPFunctionEventHandler.GetNodeValue(var Result: TsExpressionResult);
begin
if Length(FArgumentParams)>0 then
CalcParams;
FCallBack(Result, FArgumentParams);
Result.ResultType := NodeType;
end;
{ TsCellExprNode }
constructor TsCellExprNode.Create(AWorksheet: TsWorksheet; ACellString: String);
var
r, c: Cardinal;
flags: TsRelFlags;
begin
ParseCellString(ACellString, r, c, flags);
Create(AWorksheet, AWorksheet.FindCell(r, c), flags);
end;
constructor TsCellExprNode.Create(AWorksheet: TsWorksheet; ACell: PCell; AFlags: TsRelFlags);
begin
FCell := ACell;
FFlags := AFlags;
end;
function TsCellExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNCellValue(FCell^.Row, FCell^.Col, FFlags, ANext);
end;
function TsCellExprNode.AsString: string;
begin
Result := GetCellString(FCell^.Row, FCell^.Col, FFlags);
end;
procedure TsCellExprNode.Check;
begin
if not Assigned(FCell) then
RaiseParserError(SErrNoCellOperand);
if (FCell^.ContentType = cctError) and (FCell^.ErrorValue <> errOK) then
raise EExprParser.CreateFmt(SErrCellError, [AsString]);
end;
procedure TsCellExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
case FCell^.ContentType of
cctNumber:
Result.ResFloat := FCell^.NumberValue;
cctDateTime:
Result.ResDateTime := FCell^.DateTimeValue;
cctUTF8String:
Result.ResString := FCell^.UTF8StringValue;
cctBool:
Result.ResBoolean := FCell^.BoolValue;
cctEmpty:
Result.ResString := '';
end;
Result.ResultType := NodeType;
end;
function TsCellExprNode.NodeType: TsResultType;
begin
case FCell^.ContentType of
cctNumber:
Result := rtFloat;
cctDateTime:
Result := rtDateTime;
cctUTF8String:
Result := rtString;
cctBool:
Result := rtBoolean;
cctEmpty:
Result := rtString;
end;
end;
{ ---------------------------------------------------------------------
Standard Builtins support
---------------------------------------------------------------------}
{ Template for builtin.
Procedure MyCallback (Var Result : TsExpressionResult; Const Args : TExprParameterArray);
begin
end;
}
function ArgToFloat(Arg: TsExpressionResult): TExprFloat;
// Utility function for the built-in math functions. Accepts also integers
// in place of the floating point arguments. To be called in builtins or
// user-defined callbacks having float results.
begin
if Arg.ResultType = rtInteger then
result := Arg.resInteger
else
result := Arg.resFloat;
end;
// Math builtins
procedure BuiltInCos(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := cos(ArgToFloat(Args[0]));
end;
procedure BuiltInSin(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := sin(ArgToFloat(Args[0]));
end;
procedure BuiltInArcTan(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := arctan(ArgToFloat(Args[0]));
end;
procedure BuiltInAbs(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := abs(ArgToFloat(Args[0]));
end;
procedure BuiltInSqr(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := sqr(ArgToFloat(Args[0]));
end;
procedure BuiltInSqrt(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := sqrt(ArgToFloat(Args[0]));
end;
procedure BuiltInExp(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := exp(ArgToFloat(Args[0]));
end;
procedure BuiltInLn(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := ln(ArgToFloat(Args[0]));
end;
const
ln10 = ln(10);
procedure BuiltInLog(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := ln(ArgToFloat(Args[0]))/ln10;
end;
procedure BuiltInRound(var Result: TsExpressionResult; const Args: TExprParameterArray);
var
decs: Integer;
f: TExprFloat;
begin (*
decs := round(ArgToFloat(Args[1]));
f := 1.0;
while decs > 0 do begin
f := f * 10;
dec(decs);
end; *)
Result.ResInteger := round(ArgToFloat(Args[0]));
end;
procedure BuiltInTrunc(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResInteger := trunc(ArgToFloat(Args[0]));
end;
procedure BuiltInInt(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := int(ArgToFloat(Args[0]));
end;
procedure BuiltInFrac(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := frac(ArgToFloat(Args[0]));
end;
// String builtins
procedure BuiltInLength(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResInteger := Length(Args[0].ResString);
end;
procedure BuiltInCopy(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := copy(Args[0].ResString, Args[1].ResInteger, Args[2].ResInteger);
end;
procedure BuiltInDelete(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := Args[0].resString;
Delete(Result.ResString, Args[1].ResInteger, Args[2].ResInteger);
end;
procedure BuiltInPos(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResInteger := pos(Args[0].ResString, Args[1].ResString);
end;
procedure BuiltInUppercase(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := Uppercase(Args[0].ResString);
end;
procedure BuiltInLowercase(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := Lowercase(Args[0].ResString);
end;
procedure BuiltInStringReplace(var Result: TsExpressionResult; const Args: TExprParameterArray);
var
flags : TReplaceFlags;
begin
flags := [];
if Args[3].ResBoolean then
Include(flags, rfReplaceAll);
if Args[4].ResBoolean then
Include(flags, rfIgnoreCase);
Result.ResString := StringReplace(Args[0].ResString, Args[1].ResString, Args[2].ResString, flags);
end;
procedure BuiltInCompareText(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResInteger := CompareText(Args[0].ResString, Args[1].ResString);
end;
// Date/Time builtins
procedure BuiltInDate(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := Date;
end;
procedure BuiltInTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := Time;
end;
procedure BuiltInNow(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := Now;
end;
procedure BuiltInDayOfWeek(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResInteger := DayOfWeek(Args[0].resDateTime);
end;
procedure BuiltInExtractYear(var Result: TsExpressionResult; const Args: TExprParameterArray);
var
Y, M, D: Word;
begin
DecodeDate(Args[0].ResDateTime, Y, M, D);
Result.ResInteger := Y;
end;
procedure BuiltInExtractMonth(var Result: TsExpressionResult; const Args: TExprParameterArray);
var
Y, M, D: Word;
begin
DecodeDate(Args[0].ResDateTime, Y, M, D);
Result.ResInteger := M;
end;
procedure BuiltInExtractDay(var Result: TsExpressionResult; const Args: TExprParameterArray);
var
Y, M, D: Word;
begin
DecodeDate(Args[0].ResDateTime, Y, M, D);
Result.ResInteger := D;
end;
procedure BuiltInExtractHour(var Result: TsExpressionResult; const Args: TExprParameterArray);
var
H, M, S, MS: Word;
begin
DecodeTime(Args[0].ResDateTime, H, M, S, MS);
Result.ResInteger := H;
end;
procedure BuiltInExtractMin(var Result: TsExpressionResult; const Args: TExprParameterArray);
var
H, M, S, MS: word;
begin
DecodeTime(Args[0].ResDateTime, H, M, S, MS);
Result.ResInteger := M;
end;
procedure BuiltInExtractSec(var Result: TsExpressionResult; const Args: TExprParameterArray);
var
H, M, S, MS: Word;
begin
DecodeTime(Args[0].ResDateTime, H, M, S, MS);
Result.ResInteger := S;
end;
procedure BuiltInExtractMSec(var Result: TsExpressionResult; const Args: TExprParameterArray);
var
H, M, S, MS: Word;
begin
DecodeTime(Args[0].ResDateTime, H, M, S, MS);
Result.ResInteger := MS;
end;
procedure BuiltInEncodeDate(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := Encodedate(Args[0].ResInteger, Args[1].ResInteger, Args[2].ResInteger);
end;
procedure BuiltInEncodeTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := EncodeTime(Args[0].ResInteger, Args[1].ResInteger, Args[2].ResInteger, Args[3].ResInteger);
end;
procedure BuiltInEncodeDateTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := EncodeDate(Args[0].ResInteger, Args[1].ResInteger, Args[2].ResInteger)
+ EncodeTime(Args[3].ResInteger, Args[4].ResInteger, Args[5].ResInteger, Args[6].ResInteger);
end;
procedure BuiltInShortDayName(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := ShortDayNames[Args[0].ResInteger];
end;
procedure BuiltInShortMonthName(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := ShortMonthNames[Args[0].ResInteger];
end;
Procedure BuiltInLongDayName(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := LongDayNames[Args[0].ResInteger];
end;
procedure BuiltInLongMonthName(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := LongMonthNames[Args[0].ResInteger];
end;
procedure BuiltInFormatDateTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := FormatDateTime(Args[0].ResString, Args[1].ResDateTime);
end;
// Conversion
procedure BuiltInIntToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := IntToStr(Args[0].Resinteger);
end;
procedure BuiltInStrToInt(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResInteger := StrToInt(Args[0].ResString);
end;
procedure BuiltInStrToIntDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResInteger := StrToIntDef(Args[0].ResString, Args[1].ResInteger);
end;
procedure BuiltInFloatToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := FloatToStr(Args[0].ResFloat);
end;
procedure BuiltInStrToFloat(var Result: TsExpressionResult; Const Args: TExprParameterArray);
begin
Result.ResFloat := StrToFloat(Args[0].ResString);
end;
procedure BuiltInStrToFloatDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResFloat := StrToFloatDef(Args[0].ResString, Args[1].ResFloat);
end;
procedure BuiltInDateToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := DateToStr(Args[0].ResDateTime);
end;
procedure BuiltInTimeToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := TimeToStr(Args[0].ResDateTime);
end;
procedure BuiltInStrToDate(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := StrToDate(Args[0].ResString);
end;
procedure BuiltInStrToDateDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := StrToDateDef(Args[0].ResString, Args[1].ResDateTime);
end;
procedure BuiltInStrToTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := StrToTime(Args[0].ResString);
end;
procedure BuiltInStrToTimeDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := StrToTimeDef(Args[0].ResString, Args[1].ResDateTime);
end;
procedure BuiltInStrToDateTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := StrToDateTime(Args[0].ResString);
end;
procedure BuiltInStrToDateTimeDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResDateTime := StrToDateTimeDef(Args[0].ResString, Args[1].ResDateTime);
end;
procedure BuiltInBoolToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResString := BoolToStr(Args[0].ResBoolean);
end;
procedure BuiltInStrToBool(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResBoolean := StrToBool(Args[0].ResString);
end;
procedure BuiltInStrToBoolDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResBoolean := StrToBoolDef(Args[0].ResString, Args[1].ResBoolean);
end;
// Boolean
procedure BuiltInShl(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResInteger := Args[0].ResInteger shl Args[1].ResInteger
end;
procedure BuiltInShr(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
Result.ResInteger := Args[0].ResInteger shr Args[1].ResInteger
end;
procedure BuiltinIFS(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
if Args[0].ResBoolean then
Result.ResString := Args[1].ResString
else
Result.ResString := Args[2].ResString
end;
procedure BuiltinIFI(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
if Args[0].ResBoolean then
Result.ResInteger := Args[1].ResInteger
else
Result.ResInteger := Args[2].ResInteger
end;
procedure BuiltinIFF(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
if Args[0].ResBoolean then
Result.ResFloat := Args[1].ResFloat
else
Result.ResFloat := Args[2].ResFloat
end;
procedure BuiltinIFD(var Result: TsExpressionResult; const Args: TExprParameterArray);
begin
if Args[0].ResBoolean then
Result.ResDateTime := Args[1].ResDateTime
else
Result.ResDateTime := Args[2].ResDateTime
end;
procedure RegisterStdBuiltins(AManager : TsBuiltInExpressionManager);
begin
with AManager do
begin
AddFloatVariable(bcMath, 'pi', Pi);
// Math functions
AddFunction(bcMath, 'cos', 'F', 'F', @BuiltinCos);
AddFunction(bcMath, 'sin', 'F', 'F', @BuiltinSin);
AddFunction(bcMath, 'arctan', 'F', 'F', @BuiltinArctan);
AddFunction(bcMath, 'abs', 'F', 'F', @BuiltinAbs);
AddFunction(bcMath, 'sqr', 'F', 'F', @BuiltinSqr);
AddFunction(bcMath, 'sqrt', 'F', 'F', @BuiltinSqrt);
AddFunction(bcMath, 'exp', 'F', 'F', @BuiltinExp);
AddFunction(bcMath, 'ln', 'F', 'F', @BuiltinLn);
AddFunction(bcMath, 'log', 'F', 'F', @BuiltinLog);
AddFunction(bcMath, 'frac', 'F', 'F', @BuiltinFrac);
AddFunction(bcMath, 'int', 'F', 'F', @BuiltinInt);
AddFunction(bcMath, 'round', 'I', 'F', @BuiltinRound);
AddFunction(bcMath, 'trunc', 'I', 'F', @BuiltinTrunc);
// String
AddFunction(bcStrings, 'length', 'I', 'S', @BuiltinLength);
AddFunction(bcStrings, 'copy', 'S', 'SII', @BuiltinCopy);
AddFunction(bcStrings, 'delete', 'S', 'SII', @BuiltinDelete);
AddFunction(bcStrings, 'pos', 'I', 'SS', @BuiltinPos);
AddFunction(bcStrings, 'lowercase', 'S', 'S', @BuiltinLowercase);
AddFunction(bcStrings, 'uppercase', 'S', 'S', @BuiltinUppercase);
AddFunction(bcStrings, 'stringreplace','S', 'SSSBB',@BuiltinStringReplace);
AddFunction(bcStrings, 'comparetext', 'I', 'SS', @BuiltinCompareText);
// Date/Time
AddFunction(bcDateTime, 'date', 'D', '', @BuiltinDate);
AddFunction(bcDateTime, 'time', 'D', '', @BuiltinTime);
AddFunction(bcDateTime, 'now', 'D', '', @BuiltinNow);
AddFunction(bcDateTime, 'dayofweek', 'I', 'D', @BuiltinDayofweek);
AddFunction(bcDateTime, 'extractyear', 'I', 'D', @BuiltinExtractYear);
AddFunction(bcDateTime, 'extractmonth', 'I', 'D', @BuiltinExtractMonth);
AddFunction(bcDateTime, 'extractday', 'I', 'D', @BuiltinExtractDay);
AddFunction(bcDateTime, 'extracthour', 'I', 'D', @BuiltinExtractHour);
AddFunction(bcDateTime, 'extractmin', 'I', 'D', @BuiltinExtractMin);
AddFunction(bcDateTime, 'extractsec', 'I', 'D', @BuiltinExtractSec);
AddFunction(bcDateTime, 'extractmsec', 'I', 'D', @BuiltinExtractMSec);
AddFunction(bcDateTime, 'encodedate', 'D', 'III', @BuiltinEncodedate);
AddFunction(bcDateTime, 'encodetime', 'D', 'IIII',@BuiltinEncodeTime);
AddFunction(bcDateTime, 'encodedatetime', 'D', 'IIIIIII',@BuiltinEncodeDateTime);
AddFunction(bcDateTime, 'shortdayname', 'S', 'I', @BuiltinShortDayName);
AddFunction(bcDateTime, 'shortmonthname', 'S', 'I', @BuiltinShortMonthName);
AddFunction(bcDateTime, 'longdayname', 'S', 'I', @BuiltinLongDayName);
AddFunction(bcDateTime, 'longmonthname', 'S', 'I', @BuiltinLongMonthName);
AddFunction(bcDateTime, 'formatdatetime', 'S', 'SD', @BuiltinFormatDateTime);
// Boolean
AddFunction(bcBoolean, 'shl', 'I', 'II', @BuiltinShl);
AddFunction(bcBoolean, 'shr', 'I', 'II', @BuiltinShr);
AddFunction(bcBoolean, 'IFS', 'S', 'BSS', @BuiltinIFS);
AddFunction(bcBoolean, 'IFF', 'F', 'BFF', @BuiltinIFF);
AddFunction(bcBoolean, 'IFD', 'D', 'BDD', @BuiltinIFD);
AddFunction(bcBoolean, 'IFI', 'I', 'BII', @BuiltinIFI);
// Conversion
AddFunction(bcConversion, 'inttostr', 'S', 'I', @BuiltInIntToStr);
AddFunction(bcConversion, 'strtoint', 'I', 'S', @BuiltInStrToInt);
AddFunction(bcConversion, 'strtointdef', 'I', 'SI', @BuiltInStrToIntDef);
AddFunction(bcConversion, 'floattostr', 'S', 'F', @BuiltInFloatToStr);
AddFunction(bcConversion, 'strtofloat', 'F', 'S', @BuiltInStrToFloat);
AddFunction(bcConversion, 'strtofloatdef', 'F', 'SF', @BuiltInStrToFloatDef);
AddFunction(bcConversion, 'booltostr', 'S', 'B', @BuiltInBoolToStr);
AddFunction(bcConversion, 'strtobool', 'B', 'S', @BuiltInStrToBool);
AddFunction(bcConversion, 'strtobooldef', 'B', 'SB', @BuiltInStrToBoolDef);
AddFunction(bcConversion, 'datetostr', 'S', 'D', @BuiltInDateToStr);
AddFunction(bcConversion, 'timetostr', 'S', 'D', @BuiltInTimeToStr);
AddFunction(bcConversion, 'strtodate', 'D', 'S', @BuiltInStrToDate);
AddFunction(bcConversion, 'strtodatedef', 'D', 'SD', @BuiltInStrToDateDef);
AddFunction(bcConversion, 'strtotime', 'D', 'S', @BuiltInStrToTime);
AddFunction(bcConversion, 'strtotimedef', 'D', 'SD', @BuiltInStrToTimeDef);
AddFunction(bcConversion, 'strtodatetime', 'D', 'S', @BuiltInStrToDateTime);
AddFunction(bcConversion, 'strtodatetimedef', 'D', 'SD', @BuiltInStrToDateTimeDef);
end;
end;
{ TsBuiltInExprIdentifierDef }
procedure TsBuiltInExprIdentifierDef.Assign(Source: TPersistent);
begin
inherited Assign(Source);
if Source is TsBuiltInExprIdentifierDef then
FCategory:=(Source as TsBuiltInExprIdentifierDef).Category;
end;
initialization
RegisterStdBuiltins(BuiltinIdentifiers);
finalization
FreeBuiltins;
end.