{ * zExpression                                                                * }
{ ****************************************************************************** }
{ * https://zpascal.net                                                        * }
{ * https://github.com/PassByYou888/zAI                                        * }
{ * https://github.com/PassByYou888/ZServer4D                                  * }
{ * https://github.com/PassByYou888/PascalString                               * }
{ * https://github.com/PassByYou888/zRasterization                             * }
{ * https://github.com/PassByYou888/CoreCipher                                 * }
{ * https://github.com/PassByYou888/zSound                                     * }
{ * https://github.com/PassByYou888/zChinese                                   * }
{ * https://github.com/PassByYou888/zExpression                                * }
{ * https://github.com/PassByYou888/zGameWare                                  * }
{ * https://github.com/PassByYou888/zAnalysis                                  * }
{ * https://github.com/PassByYou888/FFMPEG-Header                              * }
{ * https://github.com/PassByYou888/zTranslate                                 * }
{ * https://github.com/PassByYou888/InfiniteIoT                                * }
{ * https://github.com/PassByYou888/FastMD5                                    * }
{ ****************************************************************************** }
unit zExpression;

{$INCLUDE zDefine.inc}

interface

uses SysUtils, Variants, CoreClasses, TypInfo, TextParsing, PascalStrings, UnicodeMixedLib,
  DoStatusIO, ListEngine, OpCode;

type
{$REGION 'internal define'}
  TSymbolOperation = (soAdd, soSub, soMul, soDiv, soMod, soIntDiv, soPow, soOr, soAnd, soXor, // math
    soEqual, soLessThan, soEqualOrLessThan, soGreaterThan, soEqualOrGreaterThan, soNotEqual,  // logic
    soShl, soShr,                                                                             // bit
    soBlockIndentBegin, soBlockIndentEnd,                                                     // block indent
    soPropIndentBegin, soPropIndentEnd,                                                       // property indent
    soDotSymbol, soCommaSymbol,                                                               // dot and comma
    soEolSymbol,                                                                              // eol
    soProc, soParameter,                                                                      // proc
    soUnknow);
  TSymbolOperations = set of TSymbolOperation;

  TExpressionDeclType = (
    edtSymbol,                                                                                 // symbol
    edtBool, edtInt, edtInt64, edtUInt64, edtWord, edtByte, edtSmallInt, edtShortInt, edtUInt, // inbuild byte type
    edtSingle, edtDouble, edtCurrency,                                                         // inbuild float type
    edtString,                                                                                 // string
    edtProcExp,                                                                                // proc
    edtExpressionAsValue,                                                                      // expression
    edtUnknow);

  TExpressionDeclTypes = set of TExpressionDeclType;

  TSymbolExpression = class;

  TExpressionListData = record
    DeclType: TExpressionDeclType; // declaration
    charPos: Integer;              // char pos
    Symbol: TSymbolOperation;      // symbol
    Value: Variant;                // value
    Expression: TSymbolExpression; // expression
    ExpressionAutoFree: Boolean;   // autofree
  end;

  PExpressionListData = ^TExpressionListData;

  TNumTextType = (nttBool, nttInt, nttInt64, nttUInt64, nttWord, nttByte,
    nttSmallInt, nttShortInt, nttUInt,
    nttSingle, nttDouble, nttCurrency,
    nttUnknow);

  TSymbolExpression = class sealed(TCoreClassObject)
  protected
    FList: TCoreClassList;
    FTextStyle: TTextStyle;
  public
    constructor Create(const TextStyle_: TTextStyle);
    destructor Destroy; override;

    property TextStyle: TTextStyle read FTextStyle;
    procedure Clear;
    procedure PrintDebug(const detail: Boolean; const prefix: SystemString); overload;
    procedure PrintDebug(const detail: Boolean); overload;
    function Decl(): SystemString;

    function GetCount(t: TExpressionDeclTypes): Integer;
    function GetSymbolCount(Operations: TSymbolOperations): Integer;
    function AvailValueCount: Integer;
    function Count: Integer;

    function InsertSymbol(const idx: Integer; v: TSymbolOperation; charPos: Integer): PExpressionListData;
    function Insert(const idx: Integer; v: TExpressionListData): PExpressionListData;
    procedure InsertExpression(const idx: Integer; E: TSymbolExpression);
    procedure AddExpression(const E: TSymbolExpression);
    function AddSymbol(const v: TSymbolOperation; charPos: Integer): PExpressionListData;
    function AddBool(const v: Boolean; charPos: Integer): PExpressionListData;
    function AddInt(const v: Integer; charPos: Integer): PExpressionListData;
    function AddUInt(const v: Cardinal; charPos: Integer): PExpressionListData;
    function AddInt64(const v: Int64; charPos: Integer): PExpressionListData;
    function AddUInt64(const v: UInt64; charPos: Integer): PExpressionListData;
    function AddWord(const v: Word; charPos: Integer): PExpressionListData;
    function AddByte(const v: Byte; charPos: Integer): PExpressionListData;
    function AddSmallInt(const v: SmallInt; charPos: Integer): PExpressionListData;
    function AddShortInt(const v: ShortInt; charPos: Integer): PExpressionListData;
    function AddSingle(const v: Single; charPos: Integer): PExpressionListData;
    function AddDouble(const v: Double; charPos: Integer): PExpressionListData;
    function AddCurrency(const v: Currency; charPos: Integer): PExpressionListData;
    function AddString(const v: SystemString; charPos: Integer): PExpressionListData;
    function AddFunc(const v: SystemString; charPos: Integer): PExpressionListData;
    function AddExpressionAsValue(AutoFree: Boolean; Expression: TSymbolExpression; Symbol: TSymbolOperation; Value: Variant; charPos: Integer): PExpressionListData;
    function Add(const v: TExpressionListData): PExpressionListData;
    function AddCopy(const v: TExpressionListData): PExpressionListData;

    procedure Delete(const idx: Integer);
    procedure DeleteLast;

    function Last: PExpressionListData;
    function First: PExpressionListData;

    function IndexOf(p: PExpressionListData): Integer;

    function GetItems(index: Integer): PExpressionListData;
    property Items[index: Integer]: PExpressionListData read GetItems; default;
  end;

  TOnDeclValueCall = procedure(const Decl: SystemString; var ValType: TExpressionDeclType; var Value: Variant);
  TOnDeclValueMethod = procedure(const Decl: SystemString; var ValType: TExpressionDeclType; var Value: Variant) of object;
{$IFDEF FPC}
  TOnDeclValueProc = procedure(const Decl: SystemString; var ValType: TExpressionDeclType; var Value: Variant) is nested;
{$ELSE FPC}
  TOnDeclValueProc = reference to procedure(const Decl: SystemString; var ValType: TExpressionDeclType; var Value: Variant);
{$ENDIF FPC}
  //
  { text parse support }
  TExpressionParsingState = set of (esFirst, esWaitOp, esWaitIndentEnd, esWaitPropParamIndentEnd, esWaitValue);
  PExpressionParsingState = ^TExpressionParsingState;

  { variant array vector }
  TExpressionValueVector = array of Variant;
  PExpressionValueVector = ^TExpressionValueVector;

  { aligned variant matrix }
  TExpressionValueMatrix = array of TExpressionValueVector;
  PExpressionValueMatrix = ^TExpressionValueMatrix;

  // other
function NumTextType(s: TPascalString): TNumTextType;
procedure InitExp(var v: TExpressionListData);
function dt2op(const v: TExpressionDeclType): TOpValueType;
function VariantToExpressionDeclType(var v: Variant): TExpressionDeclType;

function ParseOperationState(ParsingEng: TTextParsing;
  var cPos, bPos, ePos, BlockIndent, PropIndent: Integer; var pStates: TExpressionParsingState): TSymbolOperation;

function ParseSymbol(ParsingEng: TTextParsing; WorkSym: TSymbolExpression;
  var cPos, bPos, ePos, BlockIndent, PropIndent: Integer; pStates: PExpressionParsingState): Boolean;

function __ParseTextExpressionAsSymbol(ParsingEng: TTextParsing; const uName: SystemString;
  const OnDeclValueCall: TOnDeclValueCall; const OnDeclValueMethod: TOnDeclValueMethod; const OnDeclValueProc: TOnDeclValueProc;
  RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;

// parsing text as expression structor, backcall is TOnDeclValueCall
function ParseTextExpressionAsSymbol_C(ParsingEng: TTextParsing; const uName: SystemString;
  const OnGetValue: TOnDeclValueCall; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;

// parsing text as expression structor, backcall is TOnDeclValueMethod
function ParseTextExpressionAsSymbol_M(ParsingEng: TTextParsing; const uName: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;

// parsing text as expression structor, backcall is TOnDeclValueProc
function ParseTextExpressionAsSymbol_P(ParsingEng: TTextParsing; const uName: SystemString;
  const OnGetValue: TOnDeclValueProc; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;

// parsing text as expression structor
function ParseTextExpressionAsSymbol(SpecialAsciiToken: TListPascalString;
  TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;

// parsing text as expression structor
function ParseTextExpressionAsSymbol(TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;

// parsing text as expression structor
function ParseTextExpressionAsSymbol(SpecialAsciiToken: TListPascalString; ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;
function ParseTextExpressionAsSymbol(ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;

// parsing text as expression structor
function ParseTextExpressionAsSymbol(SpecialAsciiToken: TListPascalString; ExpressionText: SystemString): TSymbolExpression; overload;
function ParseTextExpressionAsSymbol(ExpressionText: SystemString): TSymbolExpression; overload;

// parsing text as expression structor
function ParseTextExpressionAsSymbol_M(SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;
// parsing text as expression structor
function ParseTextExpressionAsSymbol_M(TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;

// parsing text as expression structor
function ParseTextExpressionAsSymbol_C(SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueCall; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;
// parsing text as expression structor
function ParseTextExpressionAsSymbol_C(TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueCall; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;

// parsing text as expression structor
function ParseTextExpressionAsSymbol_P(SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueProc; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;
// parsing text as expression structor
function ParseTextExpressionAsSymbol_P(TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueProc; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression; overload;

// symbol priority
function RebuildLogicalPrioritySymbol(Exps: TSymbolExpression): TSymbolExpression;

// format symbol
function RebuildAllSymbol(Exps: TSymbolExpression): TSymbolExpression;

// build opCode
function BuildAsOpCode(DebugMode: Boolean; SymbExps: TSymbolExpression; const uName: SystemString; LineNo: Integer): TOpCode; overload;
function BuildAsOpCode(SymbExps: TSymbolExpression): TOpCode; overload;
function BuildAsOpCode(DebugMode: Boolean; SymbExps: TSymbolExpression): TOpCode; overload;
function BuildAsOpCode(DebugMode: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString): TOpCode; overload;
function BuildAsOpCode(TextStyle: TTextStyle; ExpressionText: SystemString): TOpCode; overload;
function BuildAsOpCode(ExpressionText: SystemString): TOpCode; overload;
function BuildAsOpCode(DebugMode: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TOpCode; overload;
function BuildAsOpCode(TextStyle: TTextStyle; ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TOpCode; overload;
function BuildAsOpCode(ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TOpCode; overload;

// Evaluate Expression
function EvaluateExpressionValue_M(UsedCache: Boolean; SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; ExpressionText: SystemString; const OnGetValue: TOnDeclValueMethod): Variant;
function EvaluateExpressionValue_C(UsedCache: Boolean; SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; ExpressionText: SystemString; const OnGetValue: TOnDeclValueCall): Variant;
function EvaluateExpressionValue_P(UsedCache: Boolean; SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; ExpressionText: SystemString; const OnGetValue: TOnDeclValueProc): Variant;
{$ENDREGION 'internal define'}

function OpCache: THashObjectList;
procedure CleanOpCache();

{ prototype: EvaluateExpressionValue }
function IsSymbolVectorExpression(ExpressionText: SystemString; TextStyle: TTextStyle; SpecialAsciiToken: TListPascalString): Boolean;
function EvaluateExpressionValue(UsedCache: Boolean;
  SpecialAsciiToken: TListPascalString; DebugMode: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): Variant; overload;
function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; DebugMode: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;

// select used Cache
function EvaluateExpressionValue(UsedCache: Boolean; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;
function EvaluateExpressionValue(UsedCache: Boolean; ExpressionText: SystemString): Variant; overload;
function EvaluateExpressionValue(UsedCache: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString): Variant; overload;
function EvaluateExpressionValue(UsedCache: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;
function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; DebugMode: Boolean; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;
function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;
function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; DebugMode: Boolean; ExpressionText: SystemString): Variant; overload;
function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; ExpressionText: SystemString): Variant; overload;
function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;

// used Cache
function EvaluateExpressionValue(ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;
function EvaluateExpressionValue(ExpressionText: SystemString): Variant; overload;
function EvaluateExpressionValue(TextStyle: TTextStyle; ExpressionText: SystemString): Variant; overload;
function EvaluateExpressionValue(TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;
function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; DebugMode: Boolean; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;
function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;
function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; DebugMode: Boolean; ExpressionText: SystemString): Variant; overload;
function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; ExpressionText: SystemString): Variant; overload;
function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant; overload;

// Evaluate multi Expression as variant Vector
function EvaluateExpressionVector(DebugMode, UsedCache: Boolean; SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueVector; overload;
function EvaluateExpressionVector(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueVector; overload;
function EvaluateExpressionVector(SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueVector; overload;
function EvaluateExpressionVector(ExpressionText: SystemString; opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueVector; overload;
function EvaluateExpressionVector(ExpressionText: SystemString; const_vl: THashVariantList): TExpressionValueVector; overload;
function EvaluateExpressionVector(ExpressionText: SystemString; TextStyle: TTextStyle): TExpressionValueVector; overload;
function EvaluateExpressionVector(ExpressionText: SystemString): TExpressionValueVector; overload;

// Evaluate multi Expression as variant matrix
function EvaluateExpressionMatrix(W, H: Integer; SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueMatrix; overload;
function EvaluateExpressionMatrix(W, H: Integer; ExpressionText: SystemString; opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueMatrix; overload;
function EvaluateExpressionMatrix(W, H: Integer; ExpressionText: SystemString; const_vl: THashVariantList): TExpressionValueMatrix; overload;
function EvaluateExpressionMatrix(W, H: Integer; ExpressionText: SystemString; TextStyle: TTextStyle): TExpressionValueMatrix; overload;
function EvaluateExpressionMatrix(W, H: Integer; ExpressionText: SystemString): TExpressionValueMatrix; overload;

// easy API
function EStr(s: U_String): U_String;
function EStrToInt(s: U_String; default: Integer): Integer;
function EStrToInt64(s: U_String; default: Int64): Int64;
function EStrToFloat(s: U_String; default: Double): Double;
function EStrToSingle(s: U_String; default: Single): Single;
function EStrToDouble(s: U_String; default: Double): Double;

// print
function ExpressionValueVectorToStr(v: TExpressionValueVector): TPascalString;
procedure DoStatusE(v: TExpressionValueVector); overload;
procedure DoStatusE(v: TExpressionValueMatrix); overload;

// test
procedure EvaluateExpressionVectorAndMatrix_test_;

implementation

var
  OpCache___: THashObjectList = nil;

{$REGION 'internal imp'}


type
  TSymbolOperationType = record
    State: TSymbolOperation;
    Decl: SystemString;
  end;

const
  MethodToken: TExpressionDeclTypes = ([edtProcExp]);

  AllExpressionValueType: TExpressionDeclTypes = ([
    edtBool, edtInt, edtInt64, edtUInt64, edtWord, edtByte, edtSmallInt, edtShortInt, edtUInt,
    edtSingle, edtDouble, edtCurrency,
    edtString, edtProcExp,
    edtExpressionAsValue]);

  SymbolOperationPriority: array [0 .. 4] of TSymbolOperations = (
    ([soAnd]),
    ([soOr, soXor]),
    ([soEqual, soLessThan, soEqualOrLessThan, soGreaterThan, soEqualOrGreaterThan, soNotEqual]),
    ([soAdd, soSub]),
    ([soMul, soDiv, soMod, soIntDiv, soShl, soShr, soPow])
    );

  AllowPrioritySymbol: TSymbolOperations = ([
    soAdd, soSub, soMul, soDiv, soMod, soIntDiv, soPow, soOr, soAnd, soXor,
    soEqual, soLessThan, soEqualOrLessThan, soGreaterThan, soEqualOrGreaterThan, soNotEqual,
    soShl, soShr,
    soDotSymbol, soCommaSymbol]);

  OpLogicalSymbol: TSymbolOperations = ([
    soAdd, soSub, soMul, soDiv, soMod, soIntDiv, soPow, soOr, soAnd, soXor,
    soEqual, soLessThan, soEqualOrLessThan, soGreaterThan, soEqualOrGreaterThan, soNotEqual,
    soShl, soShr]);

  SymbolOperationTextDecl: array [TSymbolOperation] of TSymbolOperationType = (
    (State: soAdd; Decl: '+'),
    (State: soSub; Decl: '-'),
    (State: soMul; Decl: '*'),
    (State: soDiv; Decl: '/'),
    (State: soMod; Decl: ' mod '),
    (State: soIntDiv; Decl: ' div '),
    (State: soPow; Decl: '^'),
    (State: soOr; Decl: ' or '),
    (State: soAnd; Decl: ' and '),
    (State: soXor; Decl: ' xor '),
    (State: soEqual; Decl: ' = '),
    (State: soLessThan; Decl: ' < '),
    (State: soEqualOrLessThan; Decl: ' <= '),
    (State: soGreaterThan; Decl: ' > '),
    (State: soEqualOrGreaterThan; Decl: ' => '),
    (State: soNotEqual; Decl: ' <> '),
    (State: soShl; Decl: ' shl '),
    (State: soShr; Decl: ' shr '),
    (State: soBlockIndentBegin; Decl: '('),
    (State: soBlockIndentEnd; Decl: ')'),
    (State: soPropIndentBegin; Decl: '['),
    (State: soPropIndentEnd; Decl: ']'),
    (State: soDotSymbol; Decl: '.'),
    (State: soCommaSymbol; Decl: ','),
    (State: soEolSymbol; Decl: ';'),
    (State: soProc; Decl: '|Proc|'),
    (State: soParameter; Decl: ','),
    (State: soUnknow; Decl: '?')
    );

function NumTextType(s: TPascalString): TNumTextType;
type
  TValSym = (vsSymSub, vsSymAdd, vsSymAddSub, vsSymDollar, vsDot, vsDotBeforNum, vsDotAfterNum, vsNum, vsAtoF, vsE, vsUnknow);
var
  cnt: array [TValSym] of Integer;
  v: TValSym;
  c: SystemChar;
  i: Integer;
begin
  if s.Same('true') or s.Same('false') then
      Exit(nttBool);

  for v := low(TValSym) to high(TValSym) do
      cnt[v] := 0;

  for i := 1 to s.Len do
    begin
      c := s[i];
      if CharIn(c, [c0to9]) then
        begin
          inc(cnt[vsNum]);
          if cnt[vsDot] > 0 then
              inc(cnt[vsDotAfterNum]);
        end
      else if CharIn(c, [cLoAtoF, cHiAtoF]) then
        begin
          inc(cnt[vsAtoF]);
          if CharIn(c, 'eE') then
              inc(cnt[vsE]);
        end
      else if c = '.' then
        begin
          inc(cnt[vsDot]);
          cnt[vsDotBeforNum] := cnt[vsNum];
        end
      else if CharIn(c, '-') then
        begin
          inc(cnt[vsSymSub]);
          inc(cnt[vsSymAddSub]);
        end
      else if CharIn(c, '+') then
        begin
          inc(cnt[vsSymAdd]);
          inc(cnt[vsSymAddSub]);
        end
      else if CharIn(c, '$') and (i = 1) then
        begin
          inc(cnt[vsSymDollar]);
          if i <> 1 then
              Exit(nttUnknow);
        end
      else
          Exit(nttUnknow);
    end;

  if cnt[vsDot] > 1 then
      Exit(nttUnknow);
  if cnt[vsSymDollar] > 1 then
      Exit(nttUnknow);
  if (cnt[vsSymDollar] = 0) and (cnt[vsNum] = 0) then
      Exit(nttUnknow);
  if (cnt[vsSymAdd] > 1) and (cnt[vsE] = 0) and (cnt[vsSymDollar] = 0) then
      Exit(nttUnknow);

  if (cnt[vsSymDollar] = 0) and
    ((cnt[vsDot] = 1) or ((cnt[vsE] = 1) and ((cnt[vsSymAddSub] >= 1) and (cnt[vsSymDollar] = 0)))) then
    begin
      if cnt[vsSymDollar] > 0 then
          Exit(nttUnknow);
      if (cnt[vsAtoF] <> cnt[vsE]) then
          Exit(nttUnknow);

      if cnt[vsE] = 1 then
        begin
          Result := nttDouble
        end
      else if ((cnt[vsDotBeforNum] > 0)) and (cnt[vsDotAfterNum] > 0) then
        begin
          if cnt[vsDotAfterNum] < 5 then
              Result := nttCurrency
          else if cnt[vsNum] > 7 then
              Result := nttDouble
          else
              Result := nttSingle;
        end
      else
          Exit(nttUnknow);
    end
  else
    begin
      if cnt[vsSymDollar] = 1 then
        begin
          if cnt[vsSymSub] > 0 then
            begin
              if cnt[vsNum] + cnt[vsAtoF] = 0 then
                  Result := nttUnknow
              else if cnt[vsNum] + cnt[vsAtoF] < 2 then
                  Result := nttShortInt
              else if cnt[vsNum] + cnt[vsAtoF] < 4 then
                  Result := nttSmallInt
              else if cnt[vsNum] + cnt[vsAtoF] < 7 then
                  Result := nttInt
              else if cnt[vsNum] + cnt[vsAtoF] < 13 then
                  Result := nttInt64
              else
                  Result := nttUnknow;
            end
          else
            begin
              if cnt[vsNum] + cnt[vsAtoF] = 0 then
                  Result := nttUnknow
              else if cnt[vsNum] + cnt[vsAtoF] < 3 then
                  Result := nttByte
              else if cnt[vsNum] + cnt[vsAtoF] < 5 then
                  Result := nttWord
              else if cnt[vsNum] + cnt[vsAtoF] < 8 then
                  Result := nttUInt
              else if cnt[vsNum] + cnt[vsAtoF] < 14 then
                  Result := nttUInt64
              else
                  Result := nttUnknow;
            end;
        end
      else if cnt[vsAtoF] > 0 then
          Exit(nttUnknow)
      else if cnt[vsSymSub] > 0 then
        begin
          if cnt[vsNum] = 0 then
              Result := nttUnknow
          else if cnt[vsNum] < 3 then
              Result := nttShortInt
          else if cnt[vsNum] < 5 then
              Result := nttSmallInt
          else if cnt[vsNum] < 8 then
              Result := nttInt
          else if cnt[vsNum] < 15 then
              Result := nttInt64
          else
              Result := nttUnknow;
        end
      else
        begin
          if cnt[vsNum] = 0 then
              Result := nttUnknow
          else if cnt[vsNum] < 3 then
              Result := nttByte
          else if cnt[vsNum] < 5 then
              Result := nttWord
          else if cnt[vsNum] < 8 then
              Result := nttUInt
          else if cnt[vsNum] < 16 then
              Result := nttUInt64
          else
              Result := nttUnknow;
        end;
    end;
end;

procedure InitExp(var v: TExpressionListData);
begin
  v.DeclType := edtUnknow;
  v.charPos := -1;
  v.Symbol := soUnknow;
  v.Value := NULL;
  v.Expression := nil;
  v.ExpressionAutoFree := False;
end;

function dt2op(const v: TExpressionDeclType): TOpValueType;
begin
  case v of
    edtBool: Result := ovtBool;
    edtInt: Result := ovtInt;
    edtInt64: Result := ovtInt64;
    edtUInt64: Result := ovtUInt64;
    edtWord: Result := ovtWord;
    edtByte: Result := ovtByte;
    edtSmallInt: Result := ovtSmallInt;
    edtShortInt: Result := ovtShortInt;
    edtUInt: Result := ovtUInt;
    edtSingle: Result := ovtSingle;
    edtDouble: Result := ovtDouble;
    edtCurrency: Result := ovtCurrency;
    edtString: Result := ovtString;
    edtProcExp: Result := ovtProc;
    else Result := ovtUnknow;
  end;
end;

function VariantToExpressionDeclType(var v: Variant): TExpressionDeclType;
begin
  case VarType(v) of
    varSmallInt: Result := edtSmallInt;
    varInteger: Result := edtInt;
    varSingle: Result := edtSingle;
    varDouble: Result := edtDouble;
    varCurrency: Result := edtCurrency;
    varBoolean: Result := edtBool;
    varShortInt: Result := edtShortInt;
    varByte: Result := edtByte;
    varWord: Result := edtWord;
    varLongWord: Result := edtUInt;
    varInt64: Result := edtInt64;
    varUInt64: Result := edtUInt64;
    else
      begin
        if VarIsStr(v) then
            Result := edtString
        else
            Result := edtUnknow;
      end;
  end;
end;

constructor TSymbolExpression.Create(const TextStyle_: TTextStyle);
begin
  inherited Create;
  FList := TCoreClassList.Create;
  FTextStyle := TextStyle_;
end;

destructor TSymbolExpression.Destroy;
begin
  Clear;
  DisposeObject(FList);
  inherited Destroy;
end;

procedure TSymbolExpression.Clear;
var
  i: Integer;
begin
  for i := 0 to FList.Count - 1 do
    begin
      if (PExpressionListData(FList[i])^.ExpressionAutoFree) and (PExpressionListData(FList[i])^.Expression <> nil) then
          DisposeObject(PExpressionListData(FList[i])^.Expression);

      Dispose(PExpressionListData(FList[i]));
    end;

  FList.Clear;
end;

procedure TSymbolExpression.PrintDebug(const detail: Boolean; const prefix: SystemString);
var
  i: Integer;
  p: PExpressionListData;
begin
  DoStatus(prefix + ' decl: ' + Decl());

  if detail then
    begin
      for i := 0 to Count - 1 do
        begin
          p := GetItems(i);

          DoStatus(prefix + ' id:%d exp:%s symbol:%s val:%s', [i,
            GetEnumName(TypeInfo(TExpressionDeclType), Ord(p^.DeclType)),
            GetEnumName(TypeInfo(TSymbolOperation), Ord(p^.Symbol)),
            VarToStr(p^.Value)]);

        end;

      DoStatus('');

      for i := 0 to Count - 1 do
        begin
          p := GetItems(i);
          if p^.Expression <> nil then
            if p^.Expression.Count > 0 then
                p^.Expression.PrintDebug(detail, prefix + ' -> ' + VarToStr(p^.Value));
        end;
    end;
end;

procedure TSymbolExpression.PrintDebug(const detail: Boolean);
begin
  PrintDebug(detail, '');
end;

function TSymbolExpression.Decl(): SystemString;
var
  i, j: Integer;
  p: PExpressionListData;
begin
  Result := '';
  for i := 0 to FList.Count - 1 do
    begin
      p := FList[i];
      case p^.DeclType of
        edtSymbol:
          Result := Result + SymbolOperationTextDecl[p^.Symbol].Decl;
        edtSingle, edtDouble, edtCurrency:
          Result := Result + FloatToStr(p^.Value);
        edtProcExp:
          begin
            Result := Result + VarToStr(p^.Value) + '(';
            for j := 0 to p^.Expression.Count - 1 do
              begin
                if j = 0 then
                    Result := Result + p^.Expression[j]^.Expression.Decl
                else
                    Result := Result + ',' + p^.Expression[j]^.Expression.Decl;
              end;
            Result := Result + ')';
          end;
        edtString:
          begin
            case FTextStyle of
              tsPascal: Result := Result + TTextParsing.TranslateTextToPascalDecl(VarToStr(p^.Value));
              tsC: Result := Result + TTextParsing.TranslateTextToC_Decl(VarToStr(p^.Value));
              else Result := Result + VarToStr(p^.Value);
            end;
          end;
        edtExpressionAsValue:
          begin
            case p^.Symbol of
              soBlockIndentBegin:
                Result := Format('%s%s%s%s',
                  [Result,
                  SymbolOperationTextDecl[soBlockIndentBegin].Decl,
                  p^.Expression.Decl,
                  SymbolOperationTextDecl[soBlockIndentEnd].Decl
                  ]);
              soPropIndentBegin:
                Result := Format('%s%s%s%s',
                  [Result,
                  SymbolOperationTextDecl[soPropIndentBegin].Decl,
                  p^.Expression.Decl,
                  SymbolOperationTextDecl[soPropIndentEnd].Decl
                  ]);
              soParameter:
                begin
                  Result := Format('%s%s%s%s',
                    [Result,
                    SymbolOperationTextDecl[soBlockIndentBegin].Decl,
                    p^.Expression.Decl,
                    SymbolOperationTextDecl[soBlockIndentEnd].Decl
                    ]);
                end;
              else
                Result := Result + ' !error! ';
            end;
          end;
        edtUnknow: Result := Result + ' !error! ';
        else
          Result := Result + VarToStr(p^.Value);
      end;
    end;
end;

function TSymbolExpression.GetCount(t: TExpressionDeclTypes): Integer;
var
  i: Integer;
  p: PExpressionListData;
begin
  Result := 0;
  for i := 0 to FList.Count - 1 do
    begin
      p := FList[i];
      if p^.DeclType in t then
          inc(Result);
    end;
end;

function TSymbolExpression.GetSymbolCount(Operations: TSymbolOperations): Integer;
var
  i: Integer;
  p: PExpressionListData;
begin
  Result := 0;
  for i := 0 to FList.Count - 1 do
    begin
      p := FList[i];
      if p^.DeclType = edtSymbol then
        begin
          if p^.Symbol in Operations then
              inc(Result);
        end;
    end;
end;

function TSymbolExpression.AvailValueCount: Integer;
begin
  Result := GetCount(AllExpressionValueType);
end;

function TSymbolExpression.Count: Integer;
begin
  Result := FList.Count;
end;

function TSymbolExpression.InsertSymbol(const idx: Integer; v: TSymbolOperation; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtSymbol;
  p^.charPos := charPos;
  p^.Symbol := v;
  p^.Value := v;
  FList.Insert(idx, p);
  Result := p;
end;

function TSymbolExpression.Insert(const idx: Integer; v: TExpressionListData): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  p^ := v;
  FList.Insert(idx, p);
  Result := p;
end;

procedure TSymbolExpression.InsertExpression(const idx: Integer; E: TSymbolExpression);
var
  NewList: TCoreClassList;
  i: Integer;
  p: PExpressionListData;
begin
  NewList := TCoreClassList.Create;
  NewList.Capacity := E.FList.Count + FList.Count;

  for i := 0 to idx do
      NewList.Add(FList[i]);

  for i := 0 to E.FList.Count - 1 do
    begin
      new(p);
      p^ := PExpressionListData(E.FList[i])^;
      NewList.Add(p);
    end;

  for i := idx to FList.Count - 1 do
      NewList.Add(FList[i]);

  DisposeObject(FList);
  FList := NewList;
end;

procedure TSymbolExpression.AddExpression(const E: TSymbolExpression);
var
  i: Integer;
begin
  for i := 0 to E.Count - 1 do
      AddCopy(E[i]^);
end;

function TSymbolExpression.AddSymbol(const v: TSymbolOperation; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtSymbol;
  p^.charPos := charPos;
  p^.Symbol := v;
  p^.Value := SymbolOperationTextDecl[v].Decl;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddBool(const v: Boolean; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtBool;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddInt(const v: Integer; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtInt;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddUInt(const v: Cardinal; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtUInt;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddInt64(const v: Int64; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtInt64;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddUInt64(const v: UInt64; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtUInt64;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddWord(const v: Word; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtWord;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddByte(const v: Byte; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtByte;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddSmallInt(const v: SmallInt; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtSmallInt;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddShortInt(const v: ShortInt; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtShortInt;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddSingle(const v: Single; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtSingle;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddDouble(const v: Double; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtDouble;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddCurrency(const v: Currency; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtCurrency;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddString(const v: SystemString; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtString;
  p^.charPos := charPos;
  p^.Value := v;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddFunc(const v: SystemString; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtProcExp;
  p^.charPos := charPos;
  p^.Symbol := soProc;
  p^.Value := v;
  p^.Expression := TSymbolExpression.Create(FTextStyle);
  p^.ExpressionAutoFree := True;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddExpressionAsValue(AutoFree: Boolean; Expression: TSymbolExpression; Symbol: TSymbolOperation; Value: Variant; charPos: Integer): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  InitExp(p^);
  p^.DeclType := edtExpressionAsValue;
  p^.charPos := charPos;
  p^.Symbol := Symbol;
  p^.Value := Value;
  p^.Expression := Expression;
  p^.ExpressionAutoFree := AutoFree;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.Add(const v: TExpressionListData): PExpressionListData;
var
  p: PExpressionListData;
begin
  new(p);
  p^ := v;
  p^.ExpressionAutoFree := False;
  FList.Add(p);
  Result := p;
end;

function TSymbolExpression.AddCopy(const v: TExpressionListData): PExpressionListData;
var
  p: PExpressionListData;
  i: Integer;
begin
  new(p);
  p^ := v;
  p^.ExpressionAutoFree := False;
  if v.Expression <> nil then
    begin
      p^.Expression := TSymbolExpression.Create(FTextStyle);
      p^.ExpressionAutoFree := True;
      for i := 0 to v.Expression.Count - 1 do
          p^.Expression.AddCopy(v.Expression[i]^)
    end;
  FList.Add(p);
  Result := p;
end;

procedure TSymbolExpression.Delete(const idx: Integer);
var
  p: PExpressionListData;
begin
  p := FList[idx];
  if (p^.ExpressionAutoFree) and (p^.Expression <> nil) then
      DisposeObject(p^.Expression);
  Dispose(p);
  FList.Delete(idx);
end;

procedure TSymbolExpression.DeleteLast;
begin
  Delete(Count - 1);
end;

function TSymbolExpression.Last: PExpressionListData;
begin
  Result := FList.Last;
end;

function TSymbolExpression.First: PExpressionListData;
begin
  Result := FList.First;
end;

function TSymbolExpression.IndexOf(p: PExpressionListData): Integer;
var
  i: Integer;
begin
  for i := FList.Count - 1 downto 0 do
    if FList[i] = p then
        Exit(i);
  Exit(-1);
end;

function TSymbolExpression.GetItems(index: Integer): PExpressionListData;
begin
  Result := FList[index];
end;

function ParseOperationState(ParsingEng: TTextParsing;
  var cPos, bPos, ePos, BlockIndent, PropIndent: Integer; var pStates: TExpressionParsingState): TSymbolOperation;

var
  c: SystemChar;
  Decl: TPascalString;
  p: PExpressionListData;
begin
  Result := soUnknow;
  if not(esWaitOp in pStates) then
      Exit;

  while cPos <= ParsingEng.Len do
    begin
      if ParsingEng.isComment(cPos) then
        begin
          cPos := ParsingEng.GetCommentEndPos(cPos);
          Continue;
        end;

      c := ParsingEng.ParsingData.Text[cPos];
      bPos := cPos;

      if (CharIn(c, ';')) then
        begin
          inc(cPos);
          Result := soEolSymbol;
          Exit;
        end;

      if (CharIn(c, ',')) then
        begin
          inc(cPos);
          pStates := pStates - [esWaitOp] + [esWaitValue];
          Result := soCommaSymbol;
          Exit;
        end;

      if CharIn(c, ')') then
        begin
          inc(cPos);
          if (esWaitIndentEnd in pStates) then
            begin
              dec(BlockIndent);
              if BlockIndent < 0 then
                begin
                  pStates := pStates - [esWaitOp, esWaitIndentEnd];
                  Result := soBlockIndentEnd;
                  Exit;
                end
              else if BlockIndent = 0 then
                  pStates := pStates - [esWaitIndentEnd];

              pStates := pStates + [esWaitOp];
              Result := soBlockIndentEnd;
              Exit;
            end
          else
            begin
              pStates := pStates - [esWaitOp, esWaitIndentEnd];
              Result := soBlockIndentEnd;
              Exit;
            end;
        end;

      if CharIn(c, ']') then
        begin
          inc(cPos);
          if (esWaitPropParamIndentEnd in pStates) then
            begin
              dec(PropIndent);
              if PropIndent < 0 then
                begin
                  pStates := pStates - [esWaitOp, esWaitPropParamIndentEnd];
                  Result := soPropIndentEnd;
                  Exit;
                end
              else if PropIndent = 0 then
                  pStates := pStates - [esWaitPropParamIndentEnd];

              pStates := pStates + [esWaitOp];
              Result := soPropIndentEnd;
              Exit;
            end
          else
            begin
              pStates := pStates - [esWaitOp, esWaitPropParamIndentEnd];
              Result := soPropIndentEnd;
              Exit;
            end;
        end;

      if CharIn(c, '(') then
        begin
          inc(cPos);
          inc(BlockIndent);
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue, esWaitIndentEnd];

          Result := soBlockIndentBegin;
          Exit;
        end;

      if CharIn(c, '[') then
        begin
          inc(cPos);
          inc(PropIndent);
          Result := soPropIndentBegin;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue, esWaitPropParamIndentEnd];
          Exit;
        end;

      if (ParsingEng.ComparePosStr(cPos, '>=')) or (ParsingEng.ComparePosStr(cPos, '=>')) then
        begin
          inc(cPos, 2);
          Result := soEqualOrGreaterThan;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;
      if (ParsingEng.ComparePosStr(cPos, '<=')) or (ParsingEng.ComparePosStr(cPos, '=<')) then
        begin
          inc(cPos, 2);
          Result := soEqualOrLessThan;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;
      if (ParsingEng.ComparePosStr(cPos, '<>')) or (ParsingEng.ComparePosStr(cPos, '><')) or (ParsingEng.ComparePosStr(cPos, '!=')) then
        begin
          inc(cPos, 2);
          Result := soNotEqual;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;
      if (ParsingEng.ComparePosStr(cPos, '==')) then
        begin
          inc(cPos, 2);
          Result := soEqual;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;
      if (ParsingEng.ComparePosStr(cPos, '&&')) then
        begin
          inc(cPos, 2);
          Result := soAnd;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;
      if (ParsingEng.ComparePosStr(cPos, '||')) then
        begin
          inc(cPos, 2);
          Result := soOr;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;
      if (ParsingEng.ComparePosStr(cPos, '<<')) then
        begin
          inc(cPos, 2);
          Result := soShl;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;
      if (ParsingEng.ComparePosStr(cPos, '>>')) then
        begin
          inc(cPos, 2);
          Result := soShr;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;

      if CharIn(c, '+-*/^=><.,&|%') then
        begin
          if c = '+' then
              Result := soAdd
          else if c = '-' then
              Result := soSub
          else if c = '*' then
              Result := soMul
          else if c = '/' then
              Result := soDiv
          else if c = '^' then
              Result := soPow
          else if c = '=' then
              Result := soEqual
          else if c = '>' then
              Result := soGreaterThan
          else if c = '<' then
              Result := soLessThan
          else if c = '.' then
              Result := soDotSymbol
          else if c = ',' then
              Result := soCommaSymbol
          else if c = '&' then
              Result := soAnd
          else if c = '|' then
              Result := soOr
          else if c = '%' then
              Result := soMod;
          inc(cPos);
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;

      if (ParsingEng.isAscii(cPos)) then
        begin
          bPos := cPos;
          ePos := ParsingEng.GetAsciiEndPos(cPos);
          Decl := ParsingEng.GetStr(bPos, ePos);

          if Decl.Same('or') then
              Result := soOr
          else if Decl.Same('and') then
              Result := soAnd
          else if Decl.Same('xor') then
              Result := soXor
          else if Decl.Same('div', 'idiv', 'intdiv') then
              Result := soIntDiv
          else if Decl.Same('fdiv', 'floatdiv') then
              Result := soDiv
          else if Decl.Same('mod') then
              Result := soMod
          else if Decl.Same('shl') then
              Result := soShl
          else if Decl.Same('shr') then
              Result := soShr
          else
            begin
              Result := soUnknow;
              Exit;
            end;

          cPos := ePos;
          pStates := pStates - [esWaitOp];
          pStates := pStates + [esWaitValue];
          Exit;
        end;

      if ParsingEng.isNumber(cPos) then
        begin
          Result := soUnknow;
          Exit;
        end;

      inc(cPos);
    end;
  pStates := [];
  Result := soEolSymbol;
end;

function ParseSymbol(ParsingEng: TTextParsing; WorkSym: TSymbolExpression;
  var cPos, bPos, ePos, BlockIndent, PropIndent: Integer; pStates: PExpressionParsingState): Boolean;
var
  bak_cPos: Integer;
  Decl: SystemString;
  OpState: TSymbolOperation;
  RV: Variant;
  robj: TCoreClassObject;
  p: PExpressionListData;
begin
  while cPos <= ParsingEng.Len do
    begin
      pStates^ := pStates^ - [esWaitValue, esFirst];
      pStates^ := pStates^ + [esWaitOp];

      bak_cPos := cPos;
      OpState := ParseOperationState(ParsingEng, cPos, bPos, ePos, BlockIndent, PropIndent, pStates^);

      case OpState of
        soUnknow, soEolSymbol:
          begin
            Result := False;
            Exit;
          end;
        soDotSymbol:
          begin
            Result := False;
            Exit;
          end;
        soCommaSymbol:
          begin
            WorkSym.AddSymbol(OpState, bak_cPos);
            Result := True;
            Exit;
          end;
        soPropIndentBegin:
          begin
            WorkSym.AddSymbol(OpState, bak_cPos);
            Result := True;
            Exit;
          end;
        soPropIndentEnd:
          begin
            WorkSym.AddSymbol(OpState, bak_cPos);
            Result := True;
            Exit;
          end;
        soBlockIndentEnd:
          begin
            WorkSym.AddSymbol(OpState, bak_cPos);
            Result := True;
            Exit;
          end;
        soBlockIndentBegin:
          begin
            WorkSym.AddSymbol(OpState, bak_cPos);
            Result := True;
            Exit;
          end;
        else
          begin
            WorkSym.AddSymbol(OpState, bak_cPos);
            Result := True;
            Exit;
          end;
      end;
    end;
  Result := False;
end;

function __ParseTextExpressionAsSymbol(ParsingEng: TTextParsing; const uName: SystemString;
  const OnDeclValueCall: TOnDeclValueCall; const OnDeclValueMethod: TOnDeclValueMethod;
  const OnDeclValueProc: TOnDeclValueProc;
  RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;

  procedure PrintError(const s: SystemString);
  begin
    if s = '' then
        DoStatus('declaration error "%s"', [ParsingEng.Text.Text])
    else
        DoStatus('declaration error "%s" -> [%s]', [ParsingEng.Text.Text, s]);
    DoStatus('');
  end;

  function GetDeclValue(const Decl: SystemString; var v: Variant): TExpressionDeclType;
  begin
    v := Decl;
    Result := edtProcExp;

    if Assigned(OnDeclValueCall) then
        OnDeclValueCall(Decl, Result, v);
    if Assigned(OnDeclValueMethod) then
        OnDeclValueMethod(Decl, Result, v);
    if Assigned(OnDeclValueProc) then
        OnDeclValueProc(Decl, Result, v);
  end;

  function FillProc(var ExpIndex: Integer; const Exps, procExp: TSymbolExpression): TSymbolExpression;
  var
    WasProc: Boolean;
    LocalExp, ResExp: TSymbolExpression;
    p1, p2, p: PExpressionListData;
  begin
    if ExpIndex >= Exps.Count then
      begin
        Result := nil;
        Exit;
      end;

    WasProc := procExp <> nil;

    if WasProc then
        LocalExp := procExp.AddExpressionAsValue(
        True, TSymbolExpression.Create(ParsingEng.TextStyle), soParameter, 'param_1', Exps[ExpIndex]^.charPos)^.Expression
    else
        LocalExp := TSymbolExpression.Create(ParsingEng.TextStyle);

    Result := LocalExp;

    while ExpIndex < Exps.Count do
      begin
        p1 := Exps[ExpIndex];

        if ExpIndex + 1 < Exps.Count then
            p2 := Exps[ExpIndex + 1]
        else
            p2 := nil;

        if (p1^.DeclType = edtProcExp) then
          begin
            if p2 <> nil then
              begin
                if (p2^.DeclType = edtSymbol) and (p2^.Symbol in [soBlockIndentBegin, soPropIndentBegin]) then
                  begin
                    inc(ExpIndex, 2);
                    p := LocalExp.AddFunc(p1^.Value, p1^.charPos);
                    FillProc(ExpIndex, Exps, p^.Expression);
                    Continue;
                  end;
              end
            else
              begin
                Result.AddFunc(p1^.Value, p1^.charPos);
                inc(ExpIndex);
                Continue;
              end;
          end;

        if (p1^.DeclType = edtSymbol) then
          begin
            if p1^.Symbol in [soBlockIndentBegin, soPropIndentBegin] then
              begin
                inc(ExpIndex);
                ResExp := FillProc(ExpIndex, Exps, nil);
                if ResExp <> nil then
                    LocalExp.AddExpressionAsValue(True, ResExp, soBlockIndentBegin, p1^.Symbol, p1^.charPos);
                Continue;
              end;
            if p1^.Symbol in [soBlockIndentEnd, soPropIndentEnd] then
              begin
                inc(ExpIndex);
                Exit;
              end;
            if (p1^.Symbol in [soCommaSymbol]) then
              begin
                if not WasProc then
                  begin
                    PrintError('comma Illegal');
                    Exit;
                  end;

                LocalExp := procExp.AddExpressionAsValue(True,
                  TSymbolExpression.Create(ParsingEng.TextStyle), soParameter, 'param_' + IntToStr(procExp.Count + 1),
                  Exps[ExpIndex]^.charPos)^.Expression;
                inc(ExpIndex);
                Continue;
              end;
          end;

        LocalExp.AddCopy(p1^);
        inc(ExpIndex);
      end;
  end;

var
  cPos, bPos, ePos, i: Integer;
  td: PTokenData;
  State: TExpressionParsingState;
  BlockIndent, PropIndent: Integer;
  Container: TSymbolExpression;
  te: TTextParsing;
  Decl: TPascalString;
  OpState: TSymbolOperation;
  isNumber, isSpecialSymbol, isAscii, isTextDecl, isSymbol: Boolean;
  RV: Variant;
  p: PExpressionListData;
begin
  Result := nil;

  if ParsingEng.ParsingData.Len < 1 then
      Exit;
  if ParsingEng.TokenCountT([ttTextDecl, ttNumber, ttAscii]) = 0 then
      Exit;

  cPos := 1;
  BlockIndent := 0;
  PropIndent := 0;
  State := [esFirst];
  Container := TSymbolExpression.Create(ParsingEng.TextStyle);

  while cPos <= ParsingEng.Len do
    begin
      if ParsingEng.isComment(cPos) then
        begin
          cPos := ParsingEng.GetCommentEndPos(cPos) + 1;
          Continue;
        end;

      // check esWaitOp state
      if (esWaitOp in State) and (CharIn(ParsingEng.GetChar(cPos), ParsingEng.SymbolTable)) then
        begin
          isNumber := False;
          isTextDecl := False;
          isAscii := False;
          isSymbol := True;
          bPos := cPos;
          ePos := bPos + 1;
        end
      else
        begin
          td := ParsingEng.TokenPos[cPos];
          isSpecialSymbol := td^.tokenType = ttSpecialSymbol;
          if isSpecialSymbol then
            begin
              isNumber := False;
              isTextDecl := False;
              isAscii := False;
              isSymbol := False;
            end
          else if (td^.tokenType = ttAscii) and
            (
            td^.Text.Same('and', 'or', 'xor', 'shl', 'shr')
            or
            td^.Text.Same('div', 'idiv', 'intdiv', 'fdiv', 'floatdiv')
            or
            td^.Text.Same('mod')
            ) then
            begin
              isSymbol := True;
              isNumber := False;
              isTextDecl := False;
              isAscii := False;
            end
          else
            begin
              isNumber := td^.tokenType = ttNumber;
              isTextDecl := td^.tokenType = ttTextDecl;
              isAscii := td^.tokenType = ttAscii;
              isSymbol := td^.tokenType = ttSymbol;
            end;
        end;

      if (not(esWaitOp in State)) and (isSpecialSymbol or isNumber or isTextDecl or isAscii) then
        begin
          if not((esWaitValue in State) or (esFirst in State)) then
            begin
              PrintError('');
              Break;
            end;

          bPos := cPos;
          ePos := td^.ePos;
          if (isSpecialSymbol) and (ParsingEng.GetAsciiBeginPos(ePos) <= ePos) then
              ePos := ParsingEng.GetSpecialSymbolEndPos(ParsingEng.GetAsciiEndPos(ePos));
          cPos := ePos;

          Decl := ParsingEng.GetStr(bPos, ePos);
          if isNumber then
            begin
              if Decl.ComparePos(1, '0x') then
                begin
                  Decl.DeleteFirst;
                  Decl[1] := '$';
                end;
              case NumTextType(Decl) of
                nttBool: Container.AddBool(StrToBool(Decl), bPos);
                nttInt: Container.AddInt(StrToInt(Decl), bPos);
                nttInt64: Container.AddInt64(StrToInt64(Decl), bPos);
{$IFDEF FPC}
                nttUInt64: Container.AddUInt64(StrToQWord(Decl), bPos);
{$ELSE}
                nttUInt64: Container.AddUInt64(StrToUInt64(Decl), bPos);
{$ENDIF}
                nttWord: Container.AddWord(StrToInt(Decl), bPos);
                nttByte: Container.AddByte(StrToInt(Decl), bPos);
                nttSmallInt: Container.AddSmallInt(StrToInt(Decl), bPos);
                nttShortInt: Container.AddShortInt(StrToInt(Decl), bPos);
                nttUInt: Container.AddUInt(StrToInt(Decl), bPos);
                nttSingle: Container.AddSingle(StrToFloat(Decl), bPos);
                nttDouble: Container.AddDouble(StrToFloat(Decl), bPos);
                nttCurrency: Container.AddCurrency(StrToFloat(Decl), bPos);
                else
                  begin
                    PrintError(Format('number expression "%s" Illegal', [Decl.Text]));
                    Break;
                  end;
              end;
            end
          else if isTextDecl then
            begin
              Container.AddString(ParsingEng.GetTextBody(Decl), bPos);
            end
          else
            case NumTextType(Decl) of
              nttBool: Container.AddBool(StrToBool(Decl), bPos);
              nttInt: Container.AddInt(StrToInt(Decl), bPos);
              nttInt64: Container.AddInt64(StrToInt64(Decl), bPos);
{$IFDEF FPC}
              nttUInt64: Container.AddUInt64(StrToQWord(Decl), bPos);
{$ELSE}
              nttUInt64: Container.AddUInt64(StrToUInt64(Decl), bPos);
{$ENDIF}
              nttWord: Container.AddWord(StrToInt(Decl), bPos);
              nttByte: Container.AddByte(StrToInt(Decl), bPos);
              nttSmallInt: Container.AddSmallInt(StrToInt(Decl), bPos);
              nttShortInt: Container.AddShortInt(StrToInt(Decl), bPos);
              nttUInt: Container.AddUInt(StrToInt(Decl), bPos);
              nttSingle: Container.AddSingle(StrToFloat(Decl), bPos);
              nttDouble: Container.AddDouble(StrToFloat(Decl), bPos);
              nttCurrency: Container.AddCurrency(StrToFloat(Decl), bPos);
              else
                begin
                  case GetDeclValue(Decl, RV) of
                    edtBool: Container.AddBool(RV, bPos);
                    edtInt: Container.AddInt(RV, bPos);
                    edtInt64: Container.AddInt64(RV, bPos);
                    edtUInt64: Container.AddUInt64(RV, bPos);
                    edtWord: Container.AddWord(RV, bPos);
                    edtByte: Container.AddByte(RV, bPos);
                    edtSmallInt: Container.AddSmallInt(RV, bPos);
                    edtShortInt: Container.AddShortInt(RV, bPos);
                    edtUInt: Container.AddUInt(RV, bPos);
                    edtSingle: Container.AddSingle(RV, bPos);
                    edtDouble: Container.AddDouble(RV, bPos);
                    edtCurrency: Container.AddCurrency(RV, bPos);
                    edtString: Container.AddString(RV, bPos);
                    edtProcExp:
                      begin
                        if (RefrenceOpRT <> nil) and (not RefrenceOpRT.ProcList.Exists(RV)) then
                          if (DefaultOpRT <> RefrenceOpRT) and (not DefaultOpRT.ProcList.Exists(RV)) then
                            begin
                              PrintError(Format('function "%s" Illegal', [RV]));
                              Break;
                            end;
                        Container.AddFunc(RV, bPos);
                      end;
                    else
                      begin
                        PrintError(Format('define "%s" Illegal', [Decl.Text]));
                        Break;
                      end;
                  end;
                end;
            end;
          if not ParseSymbol(ParsingEng, Container, cPos, bPos, ePos, BlockIndent, PropIndent, @State) then
              Break
          else
              Continue;
        end;

      if (isSymbol) then
        begin
          if not ParseSymbol(ParsingEng, Container, cPos, bPos, ePos, BlockIndent, PropIndent, @State) then
              Break
          else
              Continue;
        end;

      inc(cPos);
    end;

  if (BlockIndent + PropIndent = 0) then
    begin
      i := 0;
      Result := FillProc(i, Container, nil);
      if Result = nil then
          PrintError('indent error');
    end
  else
      PrintError('indent error');

  DisposeObject(Container);
end;

function ParseTextExpressionAsSymbol_C(ParsingEng: TTextParsing; const uName: SystemString;
  const OnGetValue: TOnDeclValueCall; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
begin
  Result := __ParseTextExpressionAsSymbol(ParsingEng, uName, OnGetValue, nil, nil, RefrenceOpRT);
end;

function ParseTextExpressionAsSymbol_M(ParsingEng: TTextParsing; const uName: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
begin
  Result := __ParseTextExpressionAsSymbol(ParsingEng, uName, nil, OnGetValue, nil, RefrenceOpRT);
end;

function ParseTextExpressionAsSymbol_P(ParsingEng: TTextParsing; const uName: SystemString;
  const OnGetValue: TOnDeclValueProc; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
begin
  Result := __ParseTextExpressionAsSymbol(ParsingEng, uName, nil, nil, OnGetValue, RefrenceOpRT);
end;

function ParseTextExpressionAsSymbol(SpecialAsciiToken: TListPascalString;
  TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
var
  ParsingEng: TTextParsing;
begin
  ParsingEng := TTextParsing.Create(ExpressionText, TextStyle, SpecialAsciiToken);
  Result := ParseTextExpressionAsSymbol_M(ParsingEng, uName, OnGetValue, RefrenceOpRT);
  DisposeObject(ParsingEng);
end;

function ParseTextExpressionAsSymbol(TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
begin
  Result := ParseTextExpressionAsSymbol(nil, TextStyle, uName, ExpressionText, OnGetValue, RefrenceOpRT);
end;

function ParseTextExpressionAsSymbol(SpecialAsciiToken: TListPascalString;
  ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
var
  ParsingEng: TTextParsing;
begin
  ParsingEng := TTextParsing.Create(ExpressionText, tsPascal, SpecialAsciiToken);
  Result := ParseTextExpressionAsSymbol_M(ParsingEng, '', nil, RefrenceOpRT);
  DisposeObject(ParsingEng);
end;

function ParseTextExpressionAsSymbol(ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
begin
  Result := ParseTextExpressionAsSymbol(nil, ExpressionText, RefrenceOpRT);
end;

function ParseTextExpressionAsSymbol(SpecialAsciiToken: TListPascalString; ExpressionText: SystemString): TSymbolExpression;
var
  ParsingEng: TTextParsing;
begin
  ParsingEng := TTextParsing.Create(ExpressionText, tsPascal, SpecialAsciiToken);
  Result := ParseTextExpressionAsSymbol_M(ParsingEng, '', nil, DefaultOpRT);
  DisposeObject(ParsingEng);
end;

function ParseTextExpressionAsSymbol(ExpressionText: SystemString): TSymbolExpression;
begin
  Result := ParseTextExpressionAsSymbol(nil, ExpressionText);
end;

function ParseTextExpressionAsSymbol_M(SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
var
  ParsingEng: TTextParsing;
begin
  ParsingEng := TextEngClass.Create(ExpressionText, TextStyle, SpecialAsciiToken);
  Result := ParseTextExpressionAsSymbol_M(ParsingEng, '', OnGetValue, RefrenceOpRT);
  DisposeObject(ParsingEng);
end;

function ParseTextExpressionAsSymbol_M(TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueMethod; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
begin
  Result := ParseTextExpressionAsSymbol_M(nil, TextEngClass, TextStyle, uName, ExpressionText, OnGetValue, RefrenceOpRT);
end;

function ParseTextExpressionAsSymbol_C(SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueCall; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
var
  ParsingEng: TTextParsing;
begin
  ParsingEng := TextEngClass.Create(ExpressionText, TextStyle, SpecialAsciiToken);
  Result := ParseTextExpressionAsSymbol_C(ParsingEng, '', OnGetValue, RefrenceOpRT);
  DisposeObject(ParsingEng);
end;

function ParseTextExpressionAsSymbol_C(TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueCall; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
begin
  Result := ParseTextExpressionAsSymbol_C(nil, TextEngClass, TextStyle, uName, ExpressionText, OnGetValue, RefrenceOpRT);
end;

function ParseTextExpressionAsSymbol_P(SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueProc; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
var
  ParsingEng: TTextParsing;
begin
  ParsingEng := TextEngClass.Create(ExpressionText, TextStyle, SpecialAsciiToken);
  Result := ParseTextExpressionAsSymbol_P(ParsingEng, '', OnGetValue, RefrenceOpRT);
  DisposeObject(ParsingEng);
end;

function ParseTextExpressionAsSymbol_P(TextEngClass: TTextParsingClass; TextStyle: TTextStyle; const uName, ExpressionText: SystemString;
  const OnGetValue: TOnDeclValueProc; RefrenceOpRT: TOpCustomRunTime): TSymbolExpression;
begin
  Result := ParseTextExpressionAsSymbol_P(nil, TextEngClass, TextStyle, uName, ExpressionText, OnGetValue, RefrenceOpRT);
end;

function RebuildLogicalPrioritySymbol(Exps: TSymbolExpression): TSymbolExpression;
  function SymbolPriority(s1, s2: TSymbolOperation): Integer;

    function FindSymbol(s: TSymbolOperation): Integer;
    var
      i: Integer;
    begin
      for i := low(SymbolOperationPriority) to high(SymbolOperationPriority) do
        if s in SymbolOperationPriority[i] then
            Exit(i);
      raise Exception.Create('no define symbol');
    end;

  begin
    if (s1 in [soUnknow, soCommaSymbol]) or (s2 in [soUnknow, soCommaSymbol]) then
        Exit(0);
    Result := FindSymbol(s2) - FindSymbol(s1);
  end;

var
  SymbolIndex: Integer;
  newExpression: TSymbolExpression;
  ParseAborted: Boolean;

  procedure PrintError(const s: SystemString);
  begin
    ParseAborted := True;
    if s <> '' then
        DoStatus(Format('Priority symbol failed : %s', [s]))
    else
        DoStatus('Priority symbol failed');
  end;

  procedure ProcessSymbol(OwnerSym: TSymbolOperation);
  var
    p1, p2, startIndent, lastIndent: PExpressionListData;
    LastSym, lastIndentSym: TSymbolOperation;
    LastSymbolPriority, LastOwnerSymbolPriority: Integer;
  begin
    if ParseAborted then
        Exit;
    if SymbolIndex >= Exps.Count then
        Exit;

    if newExpression.Count > 0 then
        startIndent := newExpression.Last
    else
        startIndent := nil;

    LastSym := OwnerSym;
    lastIndent := nil;
    lastIndentSym := OwnerSym;

    while True do
      begin
        if ParseAborted then
            Break;

        if SymbolIndex >= Exps.Count then
            Break;

        p1 := Exps[SymbolIndex];

        if (p1^.DeclType in AllExpressionValueType) then
          begin
            inc(SymbolIndex);
            if SymbolIndex >= Exps.Count then
              begin
                newExpression.Add(p1^);
                Break;
              end;

            p2 := Exps[SymbolIndex];

            if (p1^.DeclType in MethodToken) and (p2^.DeclType = edtExpressionAsValue) then
              begin
                newExpression.Add(p1^);
                newExpression.Add(p2^);
              end
            else if p2^.DeclType = edtSymbol then
              begin
                if p2^.Symbol in AllowPrioritySymbol then
                  begin
                    LastOwnerSymbolPriority := SymbolPriority(p2^.Symbol, OwnerSym);
                    LastSymbolPriority := SymbolPriority(p2^.Symbol, LastSym);

                    if LastOwnerSymbolPriority > 0 then
                      begin
                        newExpression.Add(p1^);
                        Break;
                      end;

                    if LastSymbolPriority < 0 then
                      begin
                        lastIndent := newExpression.AddSymbol(soBlockIndentBegin, p1^.charPos);
                        lastIndentSym := LastSym;
                        newExpression.Add(p1^);
                        newExpression.Add(p2^);

                        inc(SymbolIndex);
                        ProcessSymbol(p2^.Symbol);
                        newExpression.AddSymbol(soBlockIndentEnd, p2^.charPos);

                        Continue;
                      end
                    else if LastSymbolPriority > 0 then
                      begin
                        if startIndent = nil then
                            startIndent := newExpression.First;

                        newExpression.InsertSymbol(newExpression.IndexOf(startIndent), soBlockIndentBegin, startIndent^.charPos);
                        newExpression.Add(p1^);
                        newExpression.AddSymbol(soBlockIndentEnd, p2^.charPos);
                        newExpression.Add(p2^);
                      end
                    else
                      begin
                        newExpression.Add(p1^);
                        newExpression.Add(p2^);
                      end;
                    LastSym := p2^.Symbol;
                  end
                else
                  begin
                    PrintError(SymbolOperationTextDecl[p2^.Symbol].Decl);
                    Exit;
                  end;
              end;
          end
        else if (p1^.DeclType = edtSymbol) then
          begin
            inc(SymbolIndex);
            if SymbolIndex >= Exps.Count then
              begin
                newExpression.Add(p1^);
                Break;
              end;

            p2 := Exps[SymbolIndex];

            if (p2^.DeclType in AllExpressionValueType) then
              begin
                if p1^.Symbol in AllowPrioritySymbol then
                  begin
                    LastSymbolPriority := SymbolPriority(p1^.Symbol, lastIndentSym);

                    if LastSymbolPriority < 0 then
                      begin
                        newExpression.InsertSymbol(newExpression.IndexOf(lastIndent), soBlockIndentBegin, lastIndent^.charPos);
                        newExpression.Add(p1^);
                        LastSym := p1^.Symbol;
                        ProcessSymbol(p1^.Symbol);
                        newExpression.AddSymbol(soBlockIndentEnd, p2^.charPos);
                        Continue;
                      end
                    else
                      begin
                        newExpression.Add(p1^);
                        Continue;
                      end;
                  end
                else
                  begin
                    PrintError(SymbolOperationTextDecl[p1^.Symbol].Decl);
                    Exit;
                  end;
              end
            else
              begin
                PrintError('expression structor Illegal');
                Exit;
              end;
          end;

        inc(SymbolIndex);
      end;
  end;

begin
  Result := nil;
  if Exps.AvailValueCount = 0 then
      Exit;

  if Exps.GetSymbolCount([
    soBlockIndentBegin, soBlockIndentEnd,
    soPropIndentBegin, soPropIndentEnd,
    soEolSymbol, soUnknow]) > 0 then
    begin
      PrintError('Illegal symbol');
      Exit;
    end;

  SymbolIndex := 0;
  newExpression := TSymbolExpression.Create(Exps.FTextStyle);
  ParseAborted := False;

  ProcessSymbol(soUnknow);

  if ParseAborted then
    begin
      newExpression.Free;
      PrintError('Illegal');
    end
  else
      Result := newExpression;
end;

function RebuildAllSymbol(Exps: TSymbolExpression): TSymbolExpression;
var
  SymbolIndex: Integer;
  ParseAborted: Boolean;

  procedure PrintError(const s: SystemString);
  begin
    ParseAborted := True;
    if s <> '' then
        DoStatus(Format('indent symbol failed : %s', [s]))
    else
        DoStatus('indent symbol failed');
  end;

  function ProcessIndent(OwnerIndentSym: TSymbolOperation): TSymbolExpression;
  var
    p1, p2: PExpressionListData;
    LocalExp, ResExp: TSymbolExpression;
  begin
    LocalExp := TSymbolExpression.Create(Exps.FTextStyle);
    Result := LocalExp;
    while True do
      begin
        if SymbolIndex >= Exps.Count then
            Break;

        p1 := Exps[SymbolIndex];

        if (p1^.DeclType in [edtSymbol]) then
          begin
            if p1^.Symbol in [soBlockIndentBegin, soPropIndentBegin] then
              begin
                inc(SymbolIndex);

                ResExp := ProcessIndent(p1^.Symbol);
                LocalExp.AddExpressionAsValue(True, ResExp, p1^.Symbol, SymbolOperationTextDecl[p1^.Symbol].Decl, p1^.charPos);

                if SymbolIndex >= Exps.Count then
                  begin
                    PrintError('indent Illegal');
                    Exit;
                  end;
              end
            else if ((OwnerIndentSym = soBlockIndentBegin) and (p1^.Symbol = soBlockIndentEnd)) or
              ((OwnerIndentSym = soPropIndentBegin) and (p1^.Symbol = soPropIndentEnd)) then
              begin
                Exit;
              end
            else if p1^.Symbol in [soCommaSymbol] then
              begin
                LocalExp.Add(p1^);
              end
            else
              begin
                LocalExp.Add(p1^);
              end;
          end
        else if (p1^.DeclType in AllExpressionValueType) then
          begin
            if p1^.DeclType = edtProcExp then
              begin
                LocalExp.Add(p1^);
                inc(SymbolIndex);
                Continue;
              end;

            inc(SymbolIndex);
            if SymbolIndex >= Exps.Count then
              begin
                LocalExp.Add(p1^);
                Break;
              end;

            p2 := Exps[SymbolIndex];

            if p2^.DeclType = edtSymbol then
              begin
                if (p2^.Symbol in [soBlockIndentBegin, soPropIndentBegin]) then
                  begin
                    if (p1^.DeclType in MethodToken) then
                      begin
                        PrintError('method Illegal');
                        Exit;
                      end;

                    LocalExp.Add(p1^);
                    inc(SymbolIndex);

                    ResExp := ProcessIndent(p2^.Symbol);
                    LocalExp.AddExpressionAsValue(True, ResExp, p2^.Symbol, SymbolOperationTextDecl[p2^.Symbol].Decl, p2^.charPos);

                    if SymbolIndex >= Exps.Count then
                      begin
                        PrintError('indent Illegal');
                        Exit;
                      end;

                  end
                else if ((OwnerIndentSym = soBlockIndentBegin) and (p2^.Symbol = soBlockIndentEnd)) or
                  ((OwnerIndentSym = soPropIndentBegin) and (p2^.Symbol = soPropIndentEnd)) then
                  begin
                    LocalExp.Add(p1^);
                    Exit;
                  end
                else if p2^.Symbol = soCommaSymbol then
                  begin
                    PrintError('Comma Illegal');
                    Exit;
                  end
                else
                  begin
                    LocalExp.Add(p1^);
                    LocalExp.Add(p2^);
                  end;
              end
            else
              begin
                PrintError('expression structor Illegal');
                Exit;
              end;
          end;

        inc(SymbolIndex);
      end;
  end;

  function ProcessPriority(_e: TSymbolExpression): TSymbolExpression;
  var
    i, j: Integer;
    E, ResExp: TSymbolExpression;
    p, funcP: PExpressionListData;
  begin
    E := RebuildLogicalPrioritySymbol(_e);
    if E = nil then
      begin
        Result := nil;
        PrintError('parse priority failed');
        Exit;
      end;

    Result := TSymbolExpression.Create(E.FTextStyle);

    for i := 0 to E.Count - 1 do
      begin
        p := E[i];
        if p^.DeclType = edtExpressionAsValue then
          begin
            case p^.Symbol of
              soBlockIndentBegin:
                begin
                  Result.AddSymbol(soBlockIndentBegin, p^.charPos);
                  ResExp := ProcessPriority(p^.Expression);
                  if ResExp <> nil then
                    begin
                      Result.AddExpression(ResExp);
                      DisposeObject(ResExp);
                    end;
                  Result.AddSymbol(soBlockIndentEnd, p^.charPos);
                end;
              soPropIndentBegin:
                begin
                  Result.AddSymbol(soPropIndentBegin, p^.charPos);
                  ResExp := ProcessPriority(p^.Expression);
                  if ResExp <> nil then
                    begin
                      Result.AddExpression(ResExp);
                      DisposeObject(ResExp);
                    end;
                  Result.AddSymbol(soPropIndentEnd, p^.charPos);
                end;
              else
                begin
                  Break;
                end;
            end;
          end
        else if p^.DeclType = edtProcExp then
          begin
            funcP := Result.AddFunc(VarToStr(p^.Value), p^.charPos);
            if (p^.Expression.Count > 0) and (p^.Expression.First^.Expression.Count > 0) then
              for j := 0 to p^.Expression.Count - 1 do
                begin
                  ResExp := RebuildAllSymbol(p^.Expression[j]^.Expression);
                  if ResExp <> nil then
                      funcP^.Expression.AddExpressionAsValue(True, ResExp, soParameter, VarToStr(p^.Expression[j]^.Value), p^.Expression[j]^.charPos);
                end;
          end
        else
          begin
            Result.Add(p^);
          end;
      end;
    DisposeObject([E]);
  end;

var
  rse: TSymbolExpression;
begin
  Result := nil;
  SymbolIndex := 0;
  ParseAborted := False;

  rse := ProcessIndent(soUnknow);
  Result := ProcessPriority(rse);
  DisposeObject(rse);
end;

function BuildAsOpCode(DebugMode: Boolean; SymbExps: TSymbolExpression; const uName: SystemString; LineNo: Integer): TOpCode;
var
  NewSymbExps: TSymbolExpression;
  SymbolIndex: Integer;
  BuildAborted: Boolean;
  OpContainer: TCoreClassListForObj;

  procedure PrintError(const s: SystemString);
  begin
    BuildAborted := True;
    if s <> '' then
        DoStatus(Format('build op failed : %s', [s]))
    else
        DoStatus('build op failed');
  end;

  function NewOpValue(uName: SystemString): TOpCode;
  begin
    Result := op_Value.Create(False);
    Result.ParsedInfo := uName;
    Result.ParsedLineNo := LineNo;
    OpContainer.Add(Result);
  end;

  function NewOpProc(uName: SystemString): TOpCode;
  begin
    Result := op_Proc.Create(False);
    Result.ParsedInfo := uName;
    Result.ParsedLineNo := LineNo;
    OpContainer.Add(Result);
  end;

  function NewOpPrefixFromSym(sym: TSymbolOperation; const uName: SystemString): TOpCode;
  begin
    case sym of
      soAdd: Result := op_Add_Prefix.Create(False);
      soSub: Result := op_Sub_Prefix.Create(False);
      else
        Result := nil;
    end;
    if Result <> nil then
      begin
        Result.ParsedInfo := uName;
        Result.ParsedLineNo := LineNo;
        OpContainer.Add(Result);
      end;
  end;

  function NewOpFromSym(sym: TSymbolOperation; const uName: SystemString): TOpCode;
  begin
    case sym of
      soAdd: Result := op_Add.Create(False);
      soSub: Result := op_Sub.Create(False);
      soMul: Result := op_Mul.Create(False);
      soDiv: Result := op_Div.Create(False);
      soMod: Result := op_Mod.Create(False);
      soIntDiv: Result := op_IntDiv.Create(False);
      soPow: Result := op_Pow.Create(False);
      soOr: Result := op_Or.Create(False);
      soAnd: Result := op_And.Create(False);
      soXor: Result := op_Xor.Create(False);
      soEqual: Result := op_Equal.Create(False);
      soLessThan: Result := op_LessThan.Create(False);
      soEqualOrLessThan: Result := op_EqualOrLessThan.Create(False);
      soGreaterThan: Result := op_GreaterThan.Create(False);
      soEqualOrGreaterThan: Result := op_EqualOrGreaterThan.Create(False);
      soNotEqual: Result := op_NotEqual.Create(False);
      soShl: Result := op_Shl.Create(False);
      soShr: Result := op_Shr.Create(False);
      else
        Result := nil;
    end;
    if Result <> nil then
      begin
        Result.ParsedInfo := uName;
        Result.ParsedLineNo := LineNo;

        OpContainer.Add(Result);
      end;
  end;

  function ProcessIndent(OwnerIndentSym: TSymbolOperation): TOpCode;
  var
    i: Integer;
    p1, p2: PExpressionListData;
    LocalOp, OldOp, ResOp, ProcOp: TOpCode;
  begin
    LocalOp := nil;
    OldOp := nil;
    ResOp := nil;
    Result := nil;

    while True do
      begin
        if SymbolIndex >= NewSymbExps.Count then
          begin
            if LocalOp <> nil then
                Result := LocalOp;
            Break;
          end;

        p1 := NewSymbExps[SymbolIndex];

        if (p1^.DeclType in [edtSymbol]) then
          begin
            if p1^.Symbol in [soBlockIndentBegin, soPropIndentBegin] then
              begin
                inc(SymbolIndex);
                ResOp := ProcessIndent(p1^.Symbol);
                if ResOp <> nil then
                  begin
                    if LocalOp <> nil then
                      begin
                        LocalOp.AddLink(ResOp);
                      end
                    else
                      begin
                        LocalOp := NewOpValue(uName);
                        LocalOp.AddLink(ResOp);
                      end;
                  end
                else
                  begin
                    PrintError('logical operotion Illegal');
                    Break;
                  end;
              end
            else if ((OwnerIndentSym = soBlockIndentBegin) and (p1^.Symbol = soBlockIndentEnd)) or
              ((OwnerIndentSym = soPropIndentBegin) and (p1^.Symbol = soPropIndentEnd)) then
              begin
                Result := LocalOp;
                Break;
              end
            else if p1^.Symbol in OpLogicalSymbol then
              begin
                if LocalOp <> nil then
                  begin
                    OldOp := LocalOp;
                    LocalOp := NewOpFromSym(p1^.Symbol, uName);
                    if LocalOp = nil then
                      begin
                        PrintError('prefix symbol Illegal');
                        Break;
                      end;
                    LocalOp.AddLink(OldOp);
                  end
                else
                  begin
                    // fixed symbol prefix, -(operation), -proc(xx)...
                    if (SymbolIndex + 1 < NewSymbExps.Count) then
                      begin
                        p2 := NewSymbExps[SymbolIndex + 1];
                        if (p1^.Symbol in [soAdd, soSub]) then
                          begin
                            if (p2^.DeclType = edtSymbol) and (p2^.Symbol in [soBlockIndentBegin, soPropIndentBegin]) then
                              begin
                                inc(SymbolIndex);
                                ResOp := ProcessIndent(p2^.Symbol);
                                if ResOp <> nil then
                                  begin
                                    LocalOp := NewOpPrefixFromSym(p1^.Symbol, uName);
                                    if LocalOp = nil then
                                      begin
                                        PrintError('prefix symbol Illegal');
                                        Break;
                                      end;
                                    LocalOp.AddLink(ResOp);
                                  end
                                else
                                  begin
                                    PrintError('logical operotion Illegal');
                                    Break;
                                  end;
                                Continue;
                              end
                            else if (p2^.DeclType = edtProcExp) and (p2^.Symbol = soProc) then
                              begin
                                ProcOp := NewOpProc(uName);
                                ProcOp.AddValue(p2^.Value);
                                for i := 0 to p2^.Expression.Count - 1 do
                                  begin
                                    ResOp := BuildAsOpCode(False, p2^.Expression[i]^.Expression, uName, LineNo);
                                    if ResOp <> nil then
                                        ProcOp.AddLink(ResOp)
                                    else
                                      begin
                                        PrintError('method Illegal');
                                        Break;
                                      end;
                                  end;

                                LocalOp := NewOpPrefixFromSym(p1^.Symbol, uName);
                                LocalOp.AddLink(ProcOp);

                                inc(SymbolIndex, 2);
                                Continue;
                              end;
                          end;
                      end;
                    PrintError('logical operotion Illegal');
                    Break;
                  end;
              end
            else
              begin
                PrintError('logical operotion Illegal');
                Break;
              end;
          end
        else if (p1^.DeclType in AllExpressionValueType) then
          begin
            if p1^.DeclType = edtProcExp then
              begin
                ProcOp := NewOpProc(uName);
                ProcOp.AddValue(p1^.Value);
                for i := 0 to p1^.Expression.Count - 1 do
                  begin
                    ResOp := BuildAsOpCode(False, p1^.Expression[i]^.Expression, uName, LineNo);
                    if ResOp <> nil then
                        ProcOp.AddLink(ResOp)
                    else
                      begin
                        PrintError('method Illegal');
                        Break;
                      end;
                  end;

                if LocalOp <> nil then
                  begin
                    LocalOp.AddLink(ProcOp);
                  end
                else
                  begin
                    LocalOp := NewOpValue(uName);
                    LocalOp.AddLink(ProcOp);
                  end;
                inc(SymbolIndex);
                Continue;
              end;

            inc(SymbolIndex);
            if SymbolIndex >= NewSymbExps.Count then
              begin
                if LocalOp <> nil then
                  begin
                    LocalOp.AddValueT(p1^.Value, dt2op(p1^.DeclType));
                  end
                else
                  begin
                    LocalOp := NewOpValue(uName);
                    LocalOp.AddValueT(p1^.Value, dt2op(p1^.DeclType));
                  end;
                Result := LocalOp;
                Break;
              end;

            p2 := NewSymbExps[SymbolIndex];

            if p2^.DeclType = edtSymbol then
              begin
                if (p2^.Symbol in [soBlockIndentBegin, soPropIndentBegin]) then
                  begin
                    // function call
                    if not(p1^.DeclType in MethodToken) then
                      begin
                        PrintError('method Illegal');
                        Break;
                      end
                    else
                      begin
                      end;

                    inc(SymbolIndex);
                    ResOp := ProcessIndent(p2^.Symbol);

                  end
                else if ((OwnerIndentSym = soBlockIndentBegin) and (p2^.Symbol = soBlockIndentEnd)) or
                  ((OwnerIndentSym = soPropIndentBegin) and (p2^.Symbol = soPropIndentEnd)) then
                  begin
                    if LocalOp <> nil then
                      begin
                        LocalOp.AddValueT(p1^.Value, dt2op(p1^.DeclType));
                      end
                    else
                      begin
                        LocalOp := NewOpValue(uName);
                        LocalOp.AddValueT(p1^.Value, dt2op(p1^.DeclType));
                      end;
                    Result := LocalOp;
                    Break;
                  end
                else if p2^.Symbol in OpLogicalSymbol then
                  begin
                    if LocalOp <> nil then
                      begin
                        OldOp := LocalOp;
                        OldOp.AddValueT(p1^.Value, dt2op(p1^.DeclType));
                        LocalOp := NewOpFromSym(p2^.Symbol, uName);
                        LocalOp.AddLink(OldOp);
                      end
                    else
                      begin
                        LocalOp := NewOpFromSym(p2^.Symbol, uName);
                        LocalOp.AddValueT(p1^.Value, dt2op(p1^.DeclType));
                      end;
                  end
                else
                  begin
                    PrintError('Illegal');
                    Break;
                  end;
              end
            else
              begin
                PrintError('Illegal');
                Break;
              end;
          end;

        inc(SymbolIndex);
      end;
  end;

  procedure ProcessOpContainer(Successed: Boolean);
  var
    i: Integer;
  begin
    for i := 0 to OpContainer.Count - 1 do
      if Successed then
          TOpCode(OpContainer[i]).AutoFreeLink := True
      else
          DisposeObject(TOpCode(OpContainer[i]));
    OpContainer.Clear;
  end;

begin
  Result := nil;
  if SymbExps <> nil then
    begin
      NewSymbExps := RebuildAllSymbol(SymbExps);
      if NewSymbExps <> nil then
        begin
          if DebugMode then
              NewSymbExps.PrintDebug(True);

          if NewSymbExps.GetSymbolCount([soBlockIndentBegin, soPropIndentBegin]) =
            NewSymbExps.GetSymbolCount([soBlockIndentEnd, soPropIndentEnd]) then
            begin
              OpContainer := TCoreClassListForObj.Create;

              SymbolIndex := 0;
              BuildAborted := False;
              Result := ProcessIndent(soUnknow);
              ProcessOpContainer(Result <> nil);
              DisposeObject(OpContainer);
            end;
          DisposeObject(NewSymbExps);
        end;
    end;
end;

function BuildAsOpCode(SymbExps: TSymbolExpression): TOpCode;
begin
  Result := BuildAsOpCode(False, SymbExps, '', 0);
end;

function BuildAsOpCode(DebugMode: Boolean; SymbExps: TSymbolExpression): TOpCode;
begin
  Result := BuildAsOpCode(DebugMode, SymbExps, '', 0);
end;

function BuildAsOpCode(DebugMode: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString): TOpCode;
var
  sym: TSymbolExpression;
begin
  sym := ParseTextExpressionAsSymbol(TextStyle, '', ExpressionText, nil, DefaultOpRT);
  Result := BuildAsOpCode(DebugMode, sym, '', 0);
  DisposeObject(sym);
end;

function BuildAsOpCode(TextStyle: TTextStyle; ExpressionText: SystemString): TOpCode;
var
  sym: TSymbolExpression;
begin
  sym := ParseTextExpressionAsSymbol(TextStyle, '', ExpressionText, nil, DefaultOpRT);
  Result := BuildAsOpCode(False, sym, '', 0);
  DisposeObject(sym);
end;

function BuildAsOpCode(ExpressionText: SystemString): TOpCode;
var
  sym: TSymbolExpression;
begin
  sym := ParseTextExpressionAsSymbol(ExpressionText);
  Result := BuildAsOpCode(False, sym, '', 0);
  DisposeObject(sym);
end;

function BuildAsOpCode(DebugMode: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TOpCode;
var
  sym: TSymbolExpression;
begin
  sym := ParseTextExpressionAsSymbol(TextStyle, '', ExpressionText, nil, RefrenceOpRT);
  Result := BuildAsOpCode(DebugMode, sym, '', 0);
  DisposeObject(sym);
end;

function BuildAsOpCode(TextStyle: TTextStyle; ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TOpCode;
var
  sym: TSymbolExpression;
begin
  sym := ParseTextExpressionAsSymbol(TextStyle, '', ExpressionText, nil, RefrenceOpRT);
  Result := BuildAsOpCode(False, sym, '', 0);
  DisposeObject(sym);
end;

function BuildAsOpCode(ExpressionText: SystemString; RefrenceOpRT: TOpCustomRunTime): TOpCode;
var
  sym: TSymbolExpression;
begin
  sym := ParseTextExpressionAsSymbol(ExpressionText, RefrenceOpRT);
  Result := BuildAsOpCode(False, sym, '', 0);
  DisposeObject(sym);
end;

function EvaluateExpressionValue_M(UsedCache: Boolean; SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; ExpressionText: SystemString; const OnGetValue: TOnDeclValueMethod): Variant;
var
  sym: TSymbolExpression;
  Op: TOpCode;
  i: Integer;
begin
  Op := nil;
  if UsedCache then
    begin
      LockObject(OpCache);
      Op := TOpCode(OpCache[ExpressionText]);
      UnLockObject(OpCache);
    end;

  if (Op <> nil) and (UsedCache) then
    begin
      try
          Result := Op.Execute(DefaultOpRT);
      except
          Result := NULL;
      end;
    end
  else
    begin
      Result := NULL;
      sym := ParseTextExpressionAsSymbol_M(SpecialAsciiToken, TextEngClass, TextStyle, '', ExpressionText, OnGetValue, DefaultOpRT);

      if sym <> nil then
        begin
          Op := BuildAsOpCode(False, sym, 'Main', -1);
          if Op <> nil then
            begin
              try
                Result := Op.Execute(DefaultOpRT);
                if UsedCache then
                  begin
                    LockObject(OpCache);
                    OpCache.Add(ExpressionText, Op);
                    UnLockObject(OpCache);
                  end
                else
                    DisposeObject(Op);
              except
                  Result := NULL;
              end;
            end;
          DisposeObject(sym);
        end;
    end;
end;

function EvaluateExpressionValue_C(UsedCache: Boolean; SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; ExpressionText: SystemString; const OnGetValue: TOnDeclValueCall): Variant;
var
  sym: TSymbolExpression;
  Op: TOpCode;
  i: Integer;
begin
  Op := nil;
  if UsedCache then
    begin
      LockObject(OpCache);
      Op := TOpCode(OpCache[ExpressionText]);
      UnLockObject(OpCache);
    end;

  if (Op <> nil) and (UsedCache) then
    begin
      try
          Result := Op.Execute(DefaultOpRT);
      except
          Result := NULL;
      end;
    end
  else
    begin
      Result := NULL;
      sym := ParseTextExpressionAsSymbol_C(SpecialAsciiToken, TextEngClass, TextStyle, '', ExpressionText, OnGetValue, DefaultOpRT);

      if sym <> nil then
        begin
          Op := BuildAsOpCode(False, sym, 'Main', -1);
          if Op <> nil then
            begin
              try
                Result := Op.Execute(DefaultOpRT);
                if UsedCache then
                  begin
                    LockObject(OpCache);
                    OpCache.Add(ExpressionText, Op);
                    UnLockObject(OpCache);
                  end
                else
                    DisposeObject(Op);
              except
                  Result := NULL;
              end;
            end;
          DisposeObject(sym);
        end;
    end;
end;

function EvaluateExpressionValue_P(UsedCache: Boolean; SpecialAsciiToken: TListPascalString;
  TextEngClass: TTextParsingClass; TextStyle: TTextStyle; ExpressionText: SystemString; const OnGetValue: TOnDeclValueProc): Variant;
var
  sym: TSymbolExpression;
  Op: TOpCode;
  i: Integer;
begin
  Op := nil;
  if UsedCache then
    begin
      LockObject(OpCache);
      Op := TOpCode(OpCache[ExpressionText]);
      UnLockObject(OpCache);
    end;

  if (Op <> nil) and (UsedCache) then
    begin
      try
          Result := Op.Execute(DefaultOpRT);
      except
          Result := NULL;
      end;
    end
  else
    begin
      Result := NULL;
      sym := ParseTextExpressionAsSymbol_P(SpecialAsciiToken, TextEngClass, TextStyle, '', ExpressionText, OnGetValue, DefaultOpRT);

      if sym <> nil then
        begin
          Op := BuildAsOpCode(False, sym, 'Main', -1);
          if Op <> nil then
            begin
              try
                Result := Op.Execute(DefaultOpRT);
                if UsedCache then
                  begin
                    LockObject(OpCache);
                    OpCache.Add(ExpressionText, Op);
                    UnLockObject(OpCache);
                  end
                else
                    DisposeObject(Op);
              except
                  Result := NULL;
              end;
            end;
          DisposeObject(sym);
        end;
    end;
end;

{$ENDREGION 'internal imp'}


function OpCache: THashObjectList;
begin
  if OpCache___ = nil then
      OpCache___ := THashObjectList.CustomCreate(True, 1024 * 1024);
  Result := OpCache___;
end;

procedure CleanOpCache();
begin
  LockObject(OpCache);
  OpCache.Clear;
  UnLockObject(OpCache);
end;

type
  TExpression_ConstVL = class
    VL: THashVariantList;
    procedure GetValue(const Decl: SystemString; var ValType: TExpressionDeclType; var Value: Variant);
  end;

procedure TExpression_ConstVL.GetValue(const Decl: SystemString; var ValType: TExpressionDeclType; var Value: Variant);
begin
  if (VL <> nil) and (VL.Exists(Decl)) then
    begin
      Value := VL[Decl];
      ValType := VariantToExpressionDeclType(Value);
    end
end;

function IsSymbolVectorExpression(ExpressionText: SystemString; TextStyle: TTextStyle; SpecialAsciiToken: TListPascalString): Boolean;
var
  t: TTextParsing;
  L: TPascalStringList;
begin
  Result := False;
  t := TTextParsing.Create(umlDeleteChar(ExpressionText, #13#10#32#9), TextStyle, SpecialAsciiToken, SpacerSymbol.v);
  L := TPascalStringList.Create;
  if t.FillSymbolVector(L) then
    begin
      if (L.Count = 2) and (L[1].L = 0) then
          Result := False
      else
          Result := L.Count > 1;
    end;
  DisposeObject(t);
  DisposeObject(L);
end;

function EvaluateExpressionValue(UsedCache: Boolean;
  SpecialAsciiToken: TListPascalString; DebugMode: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): Variant;
var
  v: TExpressionValueVector;
  sym: TSymbolExpression;
  Op: TOpCode;
  i: Integer;
  exp_const_vl: TExpression_ConstVL;
begin
  if IsSymbolVectorExpression(ExpressionText, TextStyle, SpecialAsciiToken) then
    begin
      v := EvaluateExpressionVector(DebugMode, UsedCache, SpecialAsciiToken, TextStyle, ExpressionText, opRT, const_vl);
      Result := ExpressionValueVectorToStr(v).Text;
      SetLength(v, 0);
      Exit;
    end;

  Op := nil;
  if (UsedCache) and (const_vl = nil) then
    begin
      LockObject(OpCache);
      Op := TOpCode(OpCache[ExpressionText]);
      UnLockObject(OpCache);
    end;

  if (Op <> nil) and (UsedCache) and (const_vl = nil) then
    begin
      try
          Result := Op.Execute(opRT);
      except
          Result := NULL;
      end;
    end
  else
    begin
      exp_const_vl := TExpression_ConstVL.Create;
      exp_const_vl.VL := const_vl;

      Result := NULL;
      sym := ParseTextExpressionAsSymbol(SpecialAsciiToken, TextStyle, '', ExpressionText, {$IFDEF FPC}@{$ENDIF FPC}exp_const_vl.GetValue, opRT);

      if sym <> nil then
        begin
          Op := BuildAsOpCode(DebugMode, sym, 'Main', -1);
          if Op <> nil then
            begin
              try
                Result := Op.Execute(opRT);

                if (UsedCache) and (const_vl = nil) then
                  begin
                    LockObject(OpCache);
                    OpCache.Add(ExpressionText, Op);
                    UnLockObject(OpCache);
                  end
                else
                    DisposeObject(Op);
              except
                  Result := NULL;
              end;
            end;
          DisposeObject(sym);
        end
      else
        begin
        end;
      DisposeObject(exp_const_vl);
    end;
end;

function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString;
  DebugMode: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, SpecialAsciiToken, DebugMode, TextStyle, ExpressionText, opRT, nil);
end;

function EvaluateExpressionValue(UsedCache: Boolean; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, nil, False, tsPascal, ExpressionText, opRT, nil);
end;

function EvaluateExpressionValue(UsedCache: Boolean; ExpressionText: SystemString): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, nil, False, tsPascal, ExpressionText, DefaultOpRT, nil);
end;

function EvaluateExpressionValue(UsedCache: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, nil, False, TextStyle, ExpressionText, DefaultOpRT, nil);
end;

function EvaluateExpressionValue(UsedCache: Boolean; TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, nil, False, TextStyle, ExpressionText, opRT, nil);
end;

function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; DebugMode: Boolean; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, SpecialAsciiToken, DebugMode, tsPascal, ExpressionText, opRT, nil);
end;

function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, SpecialAsciiToken, False, tsPascal, ExpressionText, opRT, nil);
end;

function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; DebugMode: Boolean; ExpressionText: SystemString): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, SpecialAsciiToken, DebugMode, tsPascal, ExpressionText, DefaultOpRT, nil);
end;

function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; ExpressionText: SystemString): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, SpecialAsciiToken, False, tsPascal, ExpressionText, DefaultOpRT, nil);
end;

function EvaluateExpressionValue(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(UsedCache, SpecialAsciiToken, False, TextStyle, ExpressionText, opRT, nil);
end;

function EvaluateExpressionValue(ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(True, ExpressionText, opRT);
end;

function EvaluateExpressionValue(ExpressionText: SystemString): Variant;
begin
  Result := EvaluateExpressionValue(True, ExpressionText);
end;

function EvaluateExpressionValue(TextStyle: TTextStyle; ExpressionText: SystemString): Variant;
begin
  Result := EvaluateExpressionValue(True, nil, False, TextStyle, ExpressionText, DefaultOpRT);
end;

function EvaluateExpressionValue(TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(True, nil, False, TextStyle, ExpressionText, opRT);
end;

function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; DebugMode: Boolean; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(True, SpecialAsciiToken, DebugMode, tsPascal, ExpressionText, opRT);
end;

function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(True, SpecialAsciiToken, False, tsPascal, ExpressionText, opRT);
end;

function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; DebugMode: Boolean; ExpressionText: SystemString): Variant;
begin
  Result := EvaluateExpressionValue(True, SpecialAsciiToken, DebugMode, tsPascal, ExpressionText, DefaultOpRT);
end;

function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; ExpressionText: SystemString): Variant;
begin
  Result := EvaluateExpressionValue(True, SpecialAsciiToken, False, tsPascal, ExpressionText, DefaultOpRT);
end;

function EvaluateExpressionValue(SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString; opRT: TOpCustomRunTime): Variant;
begin
  Result := EvaluateExpressionValue(True, SpecialAsciiToken, False, TextStyle, ExpressionText, opRT);
end;

function EvaluateExpressionVector(DebugMode, UsedCache: Boolean; SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueVector;
var
  t: TTextParsing;
  L: TPascalStringList;
  i: Integer;
begin
  SetLength(Result, 0);
  if ExpressionText = '' then
      Exit;
  t := TTextParsing.Create(ExpressionText, TextStyle, SpecialAsciiToken, SpacerSymbol.v);
  L := TPascalStringList.Create;
  if t.FillSymbolVector(L) then
    begin
      SetLength(Result, L.Count);
      for i := 0 to L.Count - 1 do
        begin
          try
              Result[i] := EvaluateExpressionValue(UsedCache, SpecialAsciiToken, DebugMode, TextStyle, L[i], opRT, const_vl);
          except
              Result[i] := NULL;
          end;
        end;
    end;
  DisposeObject(L);
  DisposeObject(t);
end;

function EvaluateExpressionVector(UsedCache: Boolean; SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueVector;
begin
  Result := EvaluateExpressionVector(False, UsedCache, SpecialAsciiToken, TextStyle, ExpressionText, opRT, const_vl);
end;

function EvaluateExpressionVector(SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueVector;
begin
  Result := EvaluateExpressionVector(False, False, SpecialAsciiToken, TextStyle, ExpressionText, opRT, const_vl);
end;

function EvaluateExpressionVector(ExpressionText: SystemString; opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueVector;
begin
  Result := EvaluateExpressionVector(nil, tsPascal, ExpressionText, opRT, const_vl);
end;

function EvaluateExpressionVector(ExpressionText: SystemString; const_vl: THashVariantList): TExpressionValueVector;
begin
  Result := EvaluateExpressionVector(ExpressionText, DefaultOpRT, const_vl);
end;

function EvaluateExpressionVector(ExpressionText: SystemString; TextStyle: TTextStyle): TExpressionValueVector;
begin
  Result := EvaluateExpressionVector(nil, TextStyle, ExpressionText, nil, nil);
end;

function EvaluateExpressionVector(ExpressionText: SystemString): TExpressionValueVector;
begin
  Result := EvaluateExpressionVector(ExpressionText, nil);
end;

function EvaluateExpressionMatrix(W, H: Integer;
  SpecialAsciiToken: TListPascalString; TextStyle: TTextStyle; ExpressionText: SystemString;
  opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueMatrix; overload;
var
  buff: TExpressionValueVector;
  i, j, k: Integer;
begin
  SetLength(Result, 0, 0);
  buff := EvaluateExpressionVector(SpecialAsciiToken, TextStyle, ExpressionText, opRT, const_vl);
  if length(buff) >= W * H then
    begin
      SetLength(Result, H, W);
      k := 0;
      for j := 0 to H - 1 do
        for i := 0 to W - 1 do
          begin
            Result[j, i] := buff[k];
            inc(k);
          end;
    end;
end;

function EvaluateExpressionMatrix(W, H: Integer; ExpressionText: SystemString; opRT: TOpCustomRunTime; const_vl: THashVariantList): TExpressionValueMatrix;
begin
  Result := EvaluateExpressionMatrix(W, H, nil, tsPascal, ExpressionText, opRT, const_vl);
end;

function EvaluateExpressionMatrix(W, H: Integer; ExpressionText: SystemString; const_vl: THashVariantList): TExpressionValueMatrix;
begin
  Result := EvaluateExpressionMatrix(W, H, ExpressionText, DefaultOpRT, const_vl);
end;

function EvaluateExpressionMatrix(W, H: Integer; ExpressionText: SystemString; TextStyle: TTextStyle): TExpressionValueMatrix;
begin
  Result := EvaluateExpressionMatrix(W, H, nil, TextStyle, ExpressionText, nil, nil);
end;

function EvaluateExpressionMatrix(W, H: Integer; ExpressionText: SystemString): TExpressionValueMatrix;
begin
  Result := EvaluateExpressionMatrix(W, H, ExpressionText, DefaultOpRT, nil);
end;

function EStr(s: U_String): U_String;
begin
  Result := umlVarToStr(EvaluateExpressionValue(s), False);
end;

function EStrToInt(s: U_String; default: Integer): Integer;
var
  v: Variant;
begin
  v := EvaluateExpressionValue(s);
  if VarIsNumeric(v) then
      Result := v
  else
      Result := default;
end;

function EStrToInt64(s: U_String; default: Int64): Int64;
var
  v: Variant;
begin
  v := EvaluateExpressionValue(s);
  if VarIsNumeric(v) then
      Result := v
  else
      Result := default;
end;

function EStrToFloat(s: U_String; default: Double): Double;
begin
  Result := EStrToDouble(s, default);
end;

function EStrToSingle(s: U_String; default: Single): Single;
var
  v: Variant;
begin
  v := EvaluateExpressionValue(s);
  if VarIsNumeric(v) then
      Result := v
  else
      Result := default;
end;

function EStrToDouble(s: U_String; default: Double): Double;
var
  v: Variant;
begin
  v := EvaluateExpressionValue(s);
  if VarIsNumeric(v) then
      Result := v
  else
      Result := default;
end;

function ExpressionValueVectorToStr(v: TExpressionValueVector): TPascalString;
var
  i: Integer;
begin
  Result := '';
  for i := 0 to length(v) - 1 do
    begin
      if VarIsNull(v[i]) then
          Result.Append('error, ')
      else if VarIsStr(v[i]) then
          Result.Append(VarToStr(v[i]) + ', ')
      else
          Result.Append(VarToStr(v[i]) + ', ');
    end;
  Result := Result.TrimChar(', ');
end;

procedure DoStatusE(v: TExpressionValueVector);
var
  i: Integer;
begin
  for i := 0 to length(v) - 1 do
      DoStatusNoLn(umlVarToStr(v[i]) + ' ');
  DoStatusNoLn;
end;

procedure DoStatusE(v: TExpressionValueMatrix);
var
  i: Integer;
begin
  for i := 0 to high(v) do
      DoStatusE(v[i]);
end;

procedure EvaluateExpressionVectorAndMatrix_test_;
var
  VL: THashVariantList;
  buff: TExpressionValueVector;
  EM: TExpressionValueMatrix;
begin
  VL := THashVariantList.Create;
  VL['a1'] := 10;
  VL['a2'] := 20;
  VL['a3'] := 30;
  buff := EvaluateExpressionVector('a1,a2,a3,a1*a2,a1+a2+a3,min(a1,a2,a3)*a3', VL);
  EM := EvaluateExpressionMatrix(3, 2, 'a1,a2,a3,a1*a2,a1+a2+a3,min(a1,a2,a3)*a3', VL);
  DisposeObject(VL);
  SetLength(buff, 0);
  SetLength(EM, 0, 0);
end;

initialization

OpCache___ := nil;

finalization

DisposeObject(OpCache___);

end.
