[Tools] Removed unneeded cast

[Tools] Removed unneeded cast

// FabTools.prg
#using System
#using System.IO
#using System.Text

BEGIN NAMESPACE FabToolsNS

   CLASS FabTools

      CONSTRUCTOR()
         RETURN

        STATIC METHOD GetToken( sString := "" AS STRING, nToken := 1 AS DWORD, cSep := " " AS STRING  ) AS STRING
        //g String,String Manipulation
        //l Extract a Token.
        //d Retrieve a Token in a delimited string, using a separator. \line
        //d The separator can be a string with more than one char. If so, the full string is used as a separator.
        //x <cString> is the Source\line
        //x <nToken> is the number of the token to retrieve. ( Default is 1 )\line
        //x <cSep> is a String with the separator. ( Default is " " )\line
        //e	FabGetToken( "Hello Visual Object World !", 2, " " )	--> "Visual"
        /*
        Retrieve a Token in a delimited string, using a separator.
            FabGetToken( "Hello Visual Object World !", 2, " " )
            --> "Visual"

            The separator is a string( One or More )

        */
            LOCAL wPos		AS	DWORD
            LOCAL sVal		AS	STRING
            LOCAL nCount	AS	DWORD
            //
            sVal := ""
            nCount := 1
            //
            //
            IF ( SLen( sString ) > 0 )
                WHILE	.T.
	                wPos := At2( cSep, sString )
	                //
	                IF ( wPos > 0 )
		                // Get Token
		                sVal := SubStr3( sString, 1, wPos - 1 )
		                // Extract String
		                sString := SubStr2( sString, wPos + SLen( cSep ) )
		                //
		                WHILE ( SubStr( sString, 1, SLen( cSep ) ) == cSep )
			                // Move Next
			                sString := SubStr2( sString, SLen( cSep )+1 )
		                ENDDO
		                // Search for next Token
		                nCount ++
		                IF ( nCount > nToken )
			                EXIT
		                ENDIF
	                ELSE
		                IF ( nCount >= nToken )
			                sVal := sString
		                ELSE
			                sVal := ""
		                ENDIF
		                // Bye
		                EXIT
	                ENDIF
                ENDDO
            ENDIF
        RETURN sVal

        STATIC METHOD GMTUnixTimeToLocalUnixTime( dwUnixTime AS DWORD ) AS DWORD
        //g Date & Time
        //l Convert a GMT UnixTime to a Local UnixTime
        //p Convert a GMT UnixTime to a Local UnixTime
        //a <dwUnixTime> is a DWord with the UnixTime to convert
        //d The UnixTime is a DWord with the number of seconds elapsed since Midnight 1 Jan 1970.
        //d  It's usually exprimed in UTC time.\line
        //e // Convert UnixTime
        //e dwTime := FabGMTUnixTimeToLocalUnixTime( dwTime )
        //e // Retrieve VO Date
        //e ddate := ConDate( 1970,01,01)
        //e ddate := ddate + Integer( dwTime / 86400 )
        //e // And VO Time
        //e	cTime := FabTString( dwTime % 86400 )
        //r A Dword with the corresponding Local Time.
        LOCAL Origin AS DateTime
        LOCAL Work   AS DateTime
        LOCAL Unix   AS DateTime
        LOCAL Span   AS TimeSpan
        //
        Unix := DateTime{1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc}
        origin := DateTime{1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc}
        origin:AddSeconds(dwUnixTime)
        Work := origin:ToLocalTime()
        Span := Work - Unix
        //(DateTime.UtcNow - new DateTime(1970,1,1,0,0,0)).TotalSeconds
        RETURN (DWORD)Span:TotalSeconds

        /*
	        LOCAL PTRTZ 	IS 	_WINTIME_ZONE_INFORMATION
	        LOCAL GMTBias 	AS 	LONG
	        LOCAL StdBias	AS	LONG
	        LOCAL DlBias	AS	LONG
	        LOCAL TotalBias	AS	LONG
	        LOCAL dDate		AS	DATE
	        LOCAL nTime		AS	DWORD
	        LOCAL dStart	AS	DATE
	        LOCAL cStart	AS	STRING
	        LOCAL nStart	AS	DWORD
	        LOCAL dEnd		AS	DATE
	        LOCAL cEnd		AS	STRING
	        LOCAL nEnd		AS	DWORD
 	        // Retrieve Info about the current TimeZone
	        GetTimeZoneInformation( @ptrTZ )
	        // No StandardDate Month !!
	        IF ( ptrTZ:StandardDate:wMonth == 0 )
		        RETURN dwUnixTime
	        ENDIF
	        // Build GMT Info
	        ddate := ConDate( 1970,01,01)
	        ddate := ddate + Integer( dwUnixTime / 86400 )
	        nTime := ( dwUnixTime % 86400 )
	        // Search Start date / time of Daylight Saving
	        cStart := FabSystemTimeToVOTime( @ptrTZ:DayLightNDate )
	        nStart := Secs( cStart )
	        //Absolute Format
	        IF ( ptrTZ:DayLightNDate:wYear != 0 )
		        dStart := FabSystemTimeToVODate( @ptrTZ:DayLightNDate )
	        ELSE
		        // Day-in-Month format
		        ptrTZ:DayLightNDate:wDay := Max( ptrTZ:DayLightNDate:wDay, 1 )
		        dStart := FabGetFirstDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:DayLightNDate:wMonth, 1 ) )
		        dStart := dStart + ( 7 * ( ptrTZ:DayLightNDate:wDay - 1 ) )
		        IF ( dStart > FabGetLastDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:DayLightNDate:wMonth, 1 ) ) )
			        dStart := FabGetLastDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:DayLightNDate:wMonth, 1 ) )
		        ENDIF
	        ENDIF
	        // Search End Date / time
	        cEnd := FabSystemTimeToVOTime( @ptrTZ:StandardDate )
	        nEnd := Secs( cEnd )
	        // Absolute Format
	        IF ( ptrTZ:StandardDate:wYear != 0 )
		        dEnd := FabSystemTimeToVODate( @ptrTZ:StandardDate )
	        ELSE
		        // Day-in-Month Format
		        ptrTZ:StandardDate:wDay := Max( ptrTZ:StandardDate:wDay, 1 )
		        dEnd := FabGetFirstDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:StandardDate:wMonth, 1 ) )
		        dEnd := dEnd + ( 7 * ( ptrTZ:StandardDate:wDay - 1 ) )
		        IF ( dEnd > FabGetLastDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:StandardDate:wMonth, 1 ) ) )
			        dEnd := FabGetLastDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:StandardDate:wMonth, 1 ) )
		        ENDIF
	        ENDIF
	        // Initialize Bias
	        GMTBias := ptrTZ:Bias
	        StdBias := PTRTZ:StandardBias
	        DlBias := PTRTZ:DaylightBias
	        //
	        TotalBias := GMTBias
	        //
	        IF ( dDate == dStart )
		        IF ( nTime >= nStart )
			        TotalBias := TotalBias + DlBias
		        ENDIF
	        ELSEIF ( dDate == dEnd )
		        IF ( nTime <= nEnd )
			        TotalBias := TotalBias + DlBias
		        ENDIF
	        ELSEIF ( dDate > dStart ) .and. ( dDate < dEnd )
		        TotalBias := TotalBias + DlBias
	        ELSE
		        TotalBias := TotalBias + StdBias
	        ENDIF
	        // Apply Bias
	        IF ( long(dwUnixTime) >= TotalBias)
		        dwUnixTime := dword( long(dwUnixTime) - ( TotalBias * 60 ) )
	        ELSE
		        dwUnixTime := 0
	        ENDIF
	        //
        RETURN dwUnixTime
        */

/*        STATIC METHOD TString( dwSeconds AS DWORD ) AS STRING
        //g Date & Time
        //l Convert a specified number of seconds to a 24h time string.
        //p Convert a specified number of seconds to a 24h time string.
        //a <dwSeconds>	The number of seconds to convert.
        //r The corresponding Time
	        LOCAL cTime	AS	STRING
	        LOCAL cSep	AS	STRING
	        LOCAL nSec, nMin, nHour AS DWORD
	        //
	        cSep := Chr( GetTimeSep() )
	        nSec := ( dwSeconds % 60 )
	        nHour := Integer( dwSeconds / 3600 )
	        nMin := Integer( dwSeconds / 60 ) -( nHour * 60 )
	        cTime := StrZero( nHour, 2 ) + cSep + StrZero( nMin, 2 ) + cSep + StrZero( nSec, 2 )
	        //
        RETURN cTime
*/
        STATIC METHOD ReadPSZString( oBr AS BinaryReader ) AS STRING
            LOCAL oSB    AS StringBuilder
            LOCAL i      AS INT
            LOCAL b      AS System.Char
            LOCAL Result AS STRING
            //
            oSB := StringBuilder{ "" }
            //
            FOR i := 0 TO ( oBr:BaseStream:Length - 1 )
                b := oBr:ReadChar()
                IF ( b == 0 )
                    EXIT
                ENDIF
                oSB:Append( b )
            NEXT
            Result := oSB:ToString()
        RETURN Result

        STATIC METHOD ReadPSZString( oBr AS BinaryReader, BlocLength AS LONG ) AS STRING
            LOCAL oSB    AS StringBuilder
            LOCAL i      AS INT
            LOCAL Result AS STRING
            LOCAL Data   AS System.Char[]
            //
            Data := oBr:ReadChars( BlocLength )
            //
            i := System.Array.IndexOf(Data, (Char)0)
            oSB := StringBuilder{ "" }
            oSB:Append( Data,0, i )
            //
            Result := oSB:ToString()
        RETURN Result

        STATIC METHOD ReadPSZString( oS AS Stream ) AS STRING
            LOCAL oSB    AS StringBuilder
            LOCAL obr     AS BinaryReader
            LOCAL i      AS INT
            LOCAL b      AS System.Char
            LOCAL Result AS STRING
            //
            oSB := StringBuilder{ "" }
            obr := BinaryReader{ oS, ascenc }
            //
            FOR i := 0 TO ( oBr:BaseStream:Length - 1 )
                b := oBr:ReadChar()
                IF ( b == 0 )
                    EXIT
                ENDIF
                oSB:Append( b )
            NEXT
            Result := oSB:ToString()
        RETURN Result

        STATIC METHOD ReadPSZString( oS AS Stream, BlocLength AS LONG ) AS STRING
            LOCAL oSB    AS StringBuilder
            LOCAL oBr     AS BinaryReader
            LOCAL i      AS INT
            LOCAL Result AS STRING
            LOCAL Data   AS System.Char[]
            //
            oBr := BinaryReader{ oS, ascenc }
			//
            Data := oBr:ReadChars( BlocLength )
            //
            i := System.Array.IndexOf(Data, (Char)0)
            IF i == -1 // in a few cases strings were not null terminated
            	i := data:Length
            END IF
            oSB := StringBuilder{ "" }
            oSB:Append( Data,0, i )
            //
            Result := oSB:ToString()
        RETURN Result

/*        STATIC METHOD ExtractFileDir( FileName AS STRING) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract FileDir info from FullPath String
        //l Extract FileDir info from FullPath String
        //r the FileDir ( never ended by a \ char except for root )
        //e "C:\TEST\TESTFILE.TST"			->	"C:\TEST"
        //e "C:\TESTFILE.TST"				->	"C:\"
        //e "\TEST\TESTFILE.TST"			->	"\TEST"
        //e "TEST\TESTFILE.TST"				->	"TEST"
        //e "\\SERVER\TEST\TESTFILE.TST"	->	"\\SERVER\TEST"
	        LOCAL wPos		AS	DWORD
	        LOCAL cResult	AS	STRING
	        //
	        wPos := SLen( FileName )
	        // Starting at end of String, search for a Path or Drive separator
	        WHILE ( wPos > 0 ) .and. !InStr( SubStr3( FileName, wPos, 1 ), "\:" )
		        wPos --
	        ENDDO
	        // Don't forget to suppress separator
	        IF ( wPos > 1 ) .AND. SubStr3( FileName, wPos, 1 ) == "\" .and. !InStr( SubStr3( FileName, wPos-1, 1 ), "\:" )
		        wPos--
	        ENDIF
	        //
	        cResult := SubStr3( FileName, 1, wPos )
        RETURN cResult
*/
/*        STATIC METHOD ExtractFileDrive( FileName AS STRING ) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract FileDrive info from FullPath String
        //l Extract FileDrive info from FullPath String
        //d This functions also support FullPath information in UNC format. If so, the string
        //d  returned will hold the full UNC path.
        //r the FileDrive
        //e "C:\TEST\TESTFILE.TST"			->	"C:"
        //e "C:\TESTFILE.TST"				->	"C:"
        //e "\TEST\TESTFILE.TST"			->	""
        //e "TEST\TESTFILE.TST"				->	""
        //e "\\SERVER\TEST\TESTFILE.TST"	->	"\\SERVER\TEST"
	        LOCAL wPos		AS	DWORD
	        LOCAL wPos2		AS	DWORD
	        LOCAL cResult	AS	STRING
	        // Standard filepath string
	        IF ( SLen( FileName ) >= 3 ) .AND. ( SubStr3( FileName, 2, 1 ) == ":" )
		        cResult := Upper( SubStr3( FileName, 1, 2 ) )
	        ELSEIF ( SLen( FileName ) >= 2 ) .AND. ( SubStr3( FileName, 1, 2 ) == "\\" )
		        // UNC naming
		        wPos2 := 0
		        wPos := 3
		        WHILE ( wPos < SLen( FileName ) ) .and. ( wPos2 < 2 )
			        IF SubStr3( FileName, wPos, 1 ) == "\"
				        wPos2 ++
			        ENDIF
			        IF ( wPos2 < 2 )
				        wPos ++
			        ENDIF
		        ENDDO
		        IF ( SubStr3( FileName, wPos, 1 ) == "\" )
			        wPos --
		        ENDIF
		        cResult := SubStr3( FileName, 1, wPos )
	        ENDIF
        RETURN cResult
*/
        STATIC METHOD ExtractFileExt( FileName AS STRING) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract File Extension info from FullPath String
        //l Extract File Extension info from FullPath String
        //r the File Extension ( always start with a . char )
        //e "C:\TEST\TESTFILE.TST"			->	".TST"
        //e "C:\TEST\TESTFILE."				->	"."
        //e "C:\TEST\TESTFILE"				->	""
	        LOCAL wPos		AS	DWORD
	        LOCAL cResult	AS	STRING
	        //
	        wPos := SLen( FileName )
	        // Starting at end of String, search for a Path, Drive or extension separator
	        WHILE ( wPos > 0 ) .and. !InStr( SubStr3( FileName, wPos, 1 ), "\:." )
		        wPos --
	        ENDDO
	        //
	        IF ( wPos > 0 ) .AND. SubStr3( FileName, wPos, 1 ) == "."
		        cResult := SubStr2( FileName, wPos )
	        ENDIF
        RETURN cResult

        STATIC METHOD ExtractFileInfo( cPath AS STRING ) AS STRING
        //g Files,Files Related Classes/Functions
        //p Get File Info from a FullPath String
        //d Get File Info from a FullPath String
        //a <cPath> is a string with the FullPath information
        //r The File Name and extension stored in <cPath>
	        LOCAL cFile, cExt	AS STRING
	        //
	        cFile := FabTools.ExtractFileName( cPath )
	        cExt := FabTools.ExtractFileExt( cPath )
	        //
        RETURN ( cFile + cExt )

        STATIC METHOD ExtractFileName( FileName AS STRING) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract FileName info from FullPath String
        //l Extract FileName info from FullPath String
        //r the FileName ( including the extension )
        //e "C:\TEST\TESTFILE.TST"			->	"TESTFILE"
        //e "C:\TEST\TESTFILE."				->	"TESTFILE"
        //e "C:\TEST\TESTFILE"				->	"TESTFILE"
        //e "\\SERVER\TEST\TESTFILE.TST"	->	"TESTFILE"
	        LOCAL wPos		AS	DWORD
	        LOCAL cResult	AS	STRING
	        //
	        wPos := SLen( FileName )
	        // Starting at end of String, search for a Path or Drive separator
	        WHILE ( wPos > 0 ) .and. !InStr( SubStr3( FileName, wPos, 1 ), "\:" )
		        wPos --
	        ENDDO
	        // Extract File Name and File Extension
	        cResult := SubStr2( FileName, wPos + 1 )
	        // Now Remove the Extension
	        wPos := RAt( ".", cResult )
	        IF ( wPos > 0 )
		        cResult := SubStr( cResult, 1, wPos - 1 )
	        ENDIF
        RETURN cResult

        STATIC METHOD FabExtractFilePath( FileName AS STRING) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract FilePath info from FullPath String
        //l Extract FilePath info from FullPath String
        //r the FilePath ( always ended by a \ char )
        //e "C:\TEST\TESTFILE.TST"			->	"C:\TEST\"
        //e "C:\TESTFILE.TST"				->	"C:\"
        //e "\TEST\TESTFILE.TST"			->	"\TEST\"
        //e "TEST\TESTFILE.TST"				->	"TEST\"
        //e "\\SERVER\TEST\TESTFILE.TST"	->	"\\SERVER\TEST\"
	        LOCAL wPos		AS	DWORD
	        LOCAL cResult	AS	STRING
	        //
	        wPos := SLen( FileName )
	        // Starting at end of String, search for a Path or Drive separator
	        WHILE ( wPos > 0 ) .and. !InStr( SubStr3( FileName, wPos, 1 ), "\:" )
		        wPos --
	        ENDDO
	        //
	        cResult := SubStr3( FileName, 1, wPos )
        RETURN cResult

   END CLASS

END NAMESPACE // Fab_VO_Entities

// FabTools.prg
#using System
#using System.IO
#using System.Text

BEGIN NAMESPACE FabToolsNS

   CLASS FabTools

      CONSTRUCTOR()
         RETURN

        STATIC METHOD GetToken( sString := "" AS STRING, nToken := 1 AS DWORD, cSep := " " AS STRING  ) AS STRING
        //g String,String Manipulation
        //l Extract a Token.
        //d Retrieve a Token in a delimited string, using a separator. \line
        //d The separator can be a string with more than one char. If so, the full string is used as a separator.
        //x <cString> is the Source\line
        //x <nToken> is the number of the token to retrieve. ( Default is 1 )\line
        //x <cSep> is a String with the separator. ( Default is " " )\line
        //e	FabGetToken( "Hello Visual Object World !", 2, " " )	--> "Visual"
        /*
        Retrieve a Token in a delimited string, using a separator.
            FabGetToken( "Hello Visual Object World !", 2, " " )
            --> "Visual"

            The separator is a string( One or More )

        */
            LOCAL wPos		AS	DWORD
            LOCAL sVal		AS	STRING
            LOCAL nCount	AS	DWORD
            //
            sVal := ""
            nCount := 1
            //
            //
            IF ( SLen( sString ) > 0 )
                WHILE	.T.
	                wPos := At2( cSep, sString )
	                //
	                IF ( wPos > 0 )
		                // Get Token
		                sVal := SubStr3( sString, 1, wPos - 1 )
		                // Extract String
		                sString := SubStr2( sString, wPos + SLen( cSep ) )
		                //
		                WHILE ( SubStr( sString, 1, SLen( cSep ) ) == cSep )
			                // Move Next
			                sString := SubStr2( sString, SLen( cSep )+1 )
		                ENDDO
		                // Search for next Token
		                nCount ++
		                IF ( nCount > nToken )
			                EXIT
		                ENDIF
	                ELSE
		                IF ( nCount >= nToken )
			                sVal := sString
		                ELSE
			                sVal := ""
		                ENDIF
		                // Bye
		                EXIT
	                ENDIF
                ENDDO
            ENDIF
        RETURN sVal

        STATIC METHOD GMTUnixTimeToLocalUnixTime( dwUnixTime AS DWORD ) AS DWORD
        //g Date & Time
        //l Convert a GMT UnixTime to a Local UnixTime
        //p Convert a GMT UnixTime to a Local UnixTime
        //a <dwUnixTime> is a DWord with the UnixTime to convert
        //d The UnixTime is a DWord with the number of seconds elapsed since Midnight 1 Jan 1970.
        //d  It's usually exprimed in UTC time.\line
        //e // Convert UnixTime
        //e dwTime := FabGMTUnixTimeToLocalUnixTime( dwTime )
        //e // Retrieve VO Date
        //e ddate := ConDate( 1970,01,01)
        //e ddate := ddate + Integer( dwTime / 86400 )
        //e // And VO Time
        //e	cTime := FabTString( dwTime % 86400 )
        //r A Dword with the corresponding Local Time.
        LOCAL Origin AS DateTime
        LOCAL Work   AS DateTime
        LOCAL Unix   AS DateTime
        LOCAL Span   AS TimeSpan
        //
        Unix := DateTime{1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc}
        origin := DateTime{1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc}
        origin:AddSeconds(dwUnixTime)
        Work := origin:ToLocalTime()
        Span := Work - Unix
        //(DateTime.UtcNow - new DateTime(1970,1,1,0,0,0)).TotalSeconds
        RETURN (DWORD)Span:TotalSeconds

        /*
	        LOCAL PTRTZ 	IS 	_WINTIME_ZONE_INFORMATION
	        LOCAL GMTBias 	AS 	LONG
	        LOCAL StdBias	AS	LONG
	        LOCAL DlBias	AS	LONG
	        LOCAL TotalBias	AS	LONG
	        LOCAL dDate		AS	DATE
	        LOCAL nTime		AS	DWORD
	        LOCAL dStart	AS	DATE
	        LOCAL cStart	AS	STRING
	        LOCAL nStart	AS	DWORD
	        LOCAL dEnd		AS	DATE
	        LOCAL cEnd		AS	STRING
	        LOCAL nEnd		AS	DWORD
 	        // Retrieve Info about the current TimeZone
	        GetTimeZoneInformation( @ptrTZ )
	        // No StandardDate Month !!
	        IF ( ptrTZ:StandardDate:wMonth == 0 )
		        RETURN dwUnixTime
	        ENDIF
	        // Build GMT Info
	        ddate := ConDate( 1970,01,01)
	        ddate := ddate + Integer( dwUnixTime / 86400 )
	        nTime := ( dwUnixTime % 86400 )
	        // Search Start date / time of Daylight Saving
	        cStart := FabSystemTimeToVOTime( @ptrTZ:DayLightNDate )
	        nStart := Secs( cStart )
	        //Absolute Format
	        IF ( ptrTZ:DayLightNDate:wYear != 0 )
		        dStart := FabSystemTimeToVODate( @ptrTZ:DayLightNDate )
	        ELSE
		        // Day-in-Month format
		        ptrTZ:DayLightNDate:wDay := Max( ptrTZ:DayLightNDate:wDay, 1 )
		        dStart := FabGetFirstDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:DayLightNDate:wMonth, 1 ) )
		        dStart := dStart + ( 7 * ( ptrTZ:DayLightNDate:wDay - 1 ) )
		        IF ( dStart > FabGetLastDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:DayLightNDate:wMonth, 1 ) ) )
			        dStart := FabGetLastDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:DayLightNDate:wMonth, 1 ) )
		        ENDIF
	        ENDIF
	        // Search End Date / time
	        cEnd := FabSystemTimeToVOTime( @ptrTZ:StandardDate )
	        nEnd := Secs( cEnd )
	        // Absolute Format
	        IF ( ptrTZ:StandardDate:wYear != 0 )
		        dEnd := FabSystemTimeToVODate( @ptrTZ:StandardDate )
	        ELSE
		        // Day-in-Month Format
		        ptrTZ:StandardDate:wDay := Max( ptrTZ:StandardDate:wDay, 1 )
		        dEnd := FabGetFirstDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:StandardDate:wMonth, 1 ) )
		        dEnd := dEnd + ( 7 * ( ptrTZ:StandardDate:wDay - 1 ) )
		        IF ( dEnd > FabGetLastDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:StandardDate:wMonth, 1 ) ) )
			        dEnd := FabGetLastDOW( DOW_SUNDAY, ConDate( Year( dDate ), ptrTZ:StandardDate:wMonth, 1 ) )
		        ENDIF
	        ENDIF
	        // Initialize Bias
	        GMTBias := ptrTZ:Bias
	        StdBias := PTRTZ:StandardBias
	        DlBias := PTRTZ:DaylightBias
	        //
	        TotalBias := GMTBias
	        //
	        IF ( dDate == dStart )
		        IF ( nTime >= nStart )
			        TotalBias := TotalBias + DlBias
		        ENDIF
	        ELSEIF ( dDate == dEnd )
		        IF ( nTime <= nEnd )
			        TotalBias := TotalBias + DlBias
		        ENDIF
	        ELSEIF ( dDate > dStart ) .and. ( dDate < dEnd )
		        TotalBias := TotalBias + DlBias
	        ELSE
		        TotalBias := TotalBias + StdBias
	        ENDIF
	        // Apply Bias
	        IF ( long(dwUnixTime) >= TotalBias)
		        dwUnixTime := dword( long(dwUnixTime) - ( TotalBias * 60 ) )
	        ELSE
		        dwUnixTime := 0
	        ENDIF
	        //
        RETURN dwUnixTime
        */

/*        STATIC METHOD TString( dwSeconds AS DWORD ) AS STRING
        //g Date & Time
        //l Convert a specified number of seconds to a 24h time string.
        //p Convert a specified number of seconds to a 24h time string.
        //a <dwSeconds>	The number of seconds to convert.
        //r The corresponding Time
	        LOCAL cTime	AS	STRING
	        LOCAL cSep	AS	STRING
	        LOCAL nSec, nMin, nHour AS DWORD
	        //
	        cSep := Chr( GetTimeSep() )
	        nSec := ( dwSeconds % 60 )
	        nHour := Integer( dwSeconds / 3600 )
	        nMin := Integer( dwSeconds / 60 ) -( nHour * 60 )
	        cTime := StrZero( nHour, 2 ) + cSep + StrZero( nMin, 2 ) + cSep + StrZero( nSec, 2 )
	        //
        RETURN cTime
*/
        STATIC METHOD ReadPSZString( oBr AS BinaryReader ) AS STRING
            LOCAL oSB    AS StringBuilder
            LOCAL i      AS INT
            LOCAL b      AS System.Char
            LOCAL Result AS STRING
            //
            oSB := StringBuilder{ "" }
            //
            FOR i := 0 TO ( oBr:BaseStream:Length - 1 )
                b := oBr:ReadChar()
                IF ( b == 0 )
                    EXIT
                ENDIF
                oSB:Append( b )
            NEXT
            Result := oSB:ToString()
        RETURN Result

        STATIC METHOD ReadPSZString( oBr AS BinaryReader, BlocLength AS LONG ) AS STRING
            LOCAL oSB    AS StringBuilder
            LOCAL i      AS INT
            LOCAL Result AS STRING
            LOCAL Data   AS System.Char[]
            //
            Data := oBr:ReadChars( BlocLength )
            //
            i := System.Array.IndexOf(Data, (Char)0)
            oSB := StringBuilder{ "" }
            oSB:Append( Data,0, i )
            //
            Result := oSB:ToString()
        RETURN Result

        STATIC METHOD ReadPSZString( oS AS Stream ) AS STRING
            LOCAL oSB    AS StringBuilder
            LOCAL obr     AS BinaryReader
            LOCAL i      AS INT
            LOCAL b      AS System.Char
            LOCAL Result AS STRING
            //
            oSB := StringBuilder{ "" }
            obr := BinaryReader{ oS, ascenc }
            //
            FOR i := 0 TO ( oBr:BaseStream:Length - 1 )
                b := oBr:ReadChar()
                IF ( b == 0 )
                    EXIT
                ENDIF
                oSB:Append( b )
            NEXT
            Result := oSB:ToString()
        RETURN Result

        STATIC METHOD ReadPSZString( oS AS Stream, BlocLength AS LONG ) AS STRING
            LOCAL oSB    AS StringBuilder
            LOCAL oBr     AS BinaryReader
            LOCAL i      AS INT
            LOCAL Result AS STRING
            LOCAL Data   AS System.Char[]
            //
            oBr := BinaryReader{ oS, ascenc }
			//
            Data := oBr:ReadChars( BlocLength )
            //
            i := System.Array.IndexOf(Data, (Char)0)
            IF i == -1 // in a few cases strings were not null terminated
            	i := data:Length
            END IF
            oSB := StringBuilder{ "" }
            oSB:Append( Data,0, i )
            //
            Result := oSB:ToString()
        RETURN Result

/*        STATIC METHOD ExtractFileDir( FileName AS STRING) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract FileDir info from FullPath String
        //l Extract FileDir info from FullPath String
        //r the FileDir ( never ended by a \ char except for root )
        //e "C:\TEST\TESTFILE.TST"			->	"C:\TEST"
        //e "C:\TESTFILE.TST"				->	"C:\"
        //e "\TEST\TESTFILE.TST"			->	"\TEST"
        //e "TEST\TESTFILE.TST"				->	"TEST"
        //e "\\SERVER\TEST\TESTFILE.TST"	->	"\\SERVER\TEST"
	        LOCAL wPos		AS	DWORD
	        LOCAL cResult	AS	STRING
	        //
	        wPos := SLen( FileName )
	        // Starting at end of String, search for a Path or Drive separator
	        WHILE ( wPos > 0 ) .and. !InStr( SubStr3( FileName, wPos, 1 ), "\:" )
		        wPos --
	        ENDDO
	        // Don't forget to suppress separator
	        IF ( wPos > 1 ) .AND. SubStr3( FileName, wPos, 1 ) == "\" .and. !InStr( SubStr3( FileName, wPos-1, 1 ), "\:" )
		        wPos--
	        ENDIF
	        //
	        cResult := SubStr3( FileName, 1, wPos )
        RETURN cResult
*/
/*        STATIC METHOD ExtractFileDrive( FileName AS STRING ) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract FileDrive info from FullPath String
        //l Extract FileDrive info from FullPath String
        //d This functions also support FullPath information in UNC format. If so, the string
        //d  returned will hold the full UNC path.
        //r the FileDrive
        //e "C:\TEST\TESTFILE.TST"			->	"C:"
        //e "C:\TESTFILE.TST"				->	"C:"
        //e "\TEST\TESTFILE.TST"			->	""
        //e "TEST\TESTFILE.TST"				->	""
        //e "\\SERVER\TEST\TESTFILE.TST"	->	"\\SERVER\TEST"
	        LOCAL wPos		AS	DWORD
	        LOCAL wPos2		AS	DWORD
	        LOCAL cResult	AS	STRING
	        // Standard filepath string
	        IF ( SLen( FileName ) >= 3 ) .AND. ( SubStr3( FileName, 2, 1 ) == ":" )
		        cResult := Upper( SubStr3( FileName, 1, 2 ) )
	        ELSEIF ( SLen( FileName ) >= 2 ) .AND. ( SubStr3( FileName, 1, 2 ) == "\\" )
		        // UNC naming
		        wPos2 := 0
		        wPos := 3
		        WHILE ( wPos < SLen( FileName ) ) .and. ( wPos2 < 2 )
			        IF SubStr3( FileName, wPos, 1 ) == "\"
				        wPos2 ++
			        ENDIF
			        IF ( wPos2 < 2 )
				        wPos ++
			        ENDIF
		        ENDDO
		        IF ( SubStr3( FileName, wPos, 1 ) == "\" )
			        wPos --
		        ENDIF
		        cResult := SubStr3( FileName, 1, wPos )
	        ENDIF
        RETURN cResult
*/
        STATIC METHOD ExtractFileExt( FileName AS STRING) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract File Extension info from FullPath String
        //l Extract File Extension info from FullPath String
        //r the File Extension ( always start with a . char )
        //e "C:\TEST\TESTFILE.TST"			->	".TST"
        //e "C:\TEST\TESTFILE."				->	"."
        //e "C:\TEST\TESTFILE"				->	""
	        LOCAL wPos		AS	DWORD
	        LOCAL cResult	AS	STRING
	        //
	        wPos := SLen( FileName )
	        // Starting at end of String, search for a Path, Drive or extension separator
	        WHILE ( wPos > 0 ) .and. !InStr( SubStr3( FileName, wPos, 1 ), "\:." )
		        wPos --
	        ENDDO
	        //
	        IF ( wPos > 0 ) .AND. SubStr3( FileName, wPos, 1 ) == "."
		        cResult := SubStr2( FileName, wPos )
	        ENDIF
        RETURN cResult

        STATIC METHOD ExtractFileInfo( cPath AS STRING ) AS STRING
        //g Files,Files Related Classes/Functions
        //p Get File Info from a FullPath String
        //d Get File Info from a FullPath String
        //a <cPath> is a string with the FullPath information
        //r The File Name and extension stored in <cPath>
	        LOCAL cFile, cExt	AS STRING
	        //
	        cFile := FabTools.ExtractFileName( cPath )
	        cExt := FabTools.ExtractFileExt( cPath )
	        //
        RETURN ( cFile + cExt )

        STATIC METHOD ExtractFileName( FileName AS STRING) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract FileName info from FullPath String
        //l Extract FileName info from FullPath String
        //r the FileName ( including the extension )
        //e "C:\TEST\TESTFILE.TST"			->	"TESTFILE"
        //e "C:\TEST\TESTFILE."				->	"TESTFILE"
        //e "C:\TEST\TESTFILE"				->	"TESTFILE"
        //e "\\SERVER\TEST\TESTFILE.TST"	->	"TESTFILE"
	        LOCAL wPos		AS	DWORD
	        LOCAL cResult	AS	STRING
	        //
	        wPos := SLen( FileName )
	        // Starting at end of String, search for a Path or Drive separator
	        WHILE ( wPos > 0 ) .and. !InStr( SubStr3( FileName, wPos, 1 ), "\:" )
		        wPos --
	        ENDDO
	        // Extract File Name and File Extension
	        cResult := SubStr2( FileName, wPos + 1 )
	        // Now Remove the Extension
	        wPos := RAt( ".", cResult )
	        IF ( wPos > 0 )
		        cResult := SubStr( cResult, 1, (INT) wPos - 1 )
	        ENDIF
        RETURN cResult

        STATIC METHOD FabExtractFilePath( FileName AS STRING) AS STRING
        //g Files,Files Related Classes/Functions
        //p Extract FilePath info from FullPath String
        //l Extract FilePath info from FullPath String
        //r the FilePath ( always ended by a \ char )
        //e "C:\TEST\TESTFILE.TST"			->	"C:\TEST\"
        //e "C:\TESTFILE.TST"				->	"C:\"
        //e "\TEST\TESTFILE.TST"			->	"\TEST\"
        //e "TEST\TESTFILE.TST"				->	"TEST\"
        //e "\\SERVER\TEST\TESTFILE.TST"	->	"\\SERVER\TEST\"
	        LOCAL wPos		AS	DWORD
	        LOCAL cResult	AS	STRING
	        //
	        wPos := SLen( FileName )
	        // Starting at end of String, search for a Path or Drive separator
	        WHILE ( wPos > 0 ) .and. !InStr( SubStr3( FileName, wPos, 1 ), "\:" )
		        wPos --
	        ENDDO
	        //
	        cResult := SubStr3( FileName, 1, wPos )
        RETURN cResult

   END CLASS

END NAMESPACE // Fab_VO_Entities


modernize

modernize

-- This extended example is an Omega port of Conor McBride's First Order Unification by Structural Recursion
-- adapted to the recent Omega dialect.
-- From the paper "GADTs + Extensible Kinds = Dependent Programming"

-------- MAYBE MONAD --------------------------------
return x = Just x
fail s = Nothing
bind Nothing g = Nothing
bind (Just x) g = g x
liftM :: (a -> b) -> Maybe a -> Maybe b
liftM2 :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
liftM f ma = do { a <- ma; return (f a) }
liftM2 f ma mb = do { a <- ma; b <- mb; return (f a b) }

------- FINITE (VARIABLE) SETS & TERMS --------------
data Fin:: Nat ~> *0 where
  Fz:: Fin (S m)
  Fs:: Fin m -> Fin (S m)
 deriving Nat(f)

data Term n = Var (Fin n) | Leaf | Fork (Term n) (Term n)

------- SUBSTITUTIONS -------------------------------
rename2sub :: (Fin m -> Fin n) -> Fin m -> Term n
rename2sub f i = Var (f i)
subst :: (Fin m -> Term n) -> Term m -> Term n
subst sub (Var x) = sub x
subst sub Leaf = Leaf
subst sub (Fork s t) = Fork(subst sub s)(subst sub t)
compose :: (Fin m -> Term n) -> (Fin l -> Term m) -> (Fin l -> Term n)
compose f g i = subst f (g i)

------- OCCURS CHECK --------------------------------
thick :: Nat' n -> Fin (S n) -> Fin (S n) -> Maybe (Fin n)
thick n (Fz) (Fz) = Nothing
thick n (Fz) (Fs y) = Just y
thick (S n) (Fs x) (Fz) = Just Fz
thick (S n) (Fs x) (Fs y) = liftM Fs (thick n x y)

chk :: Nat' n -> Fin (S n) -> Term (S n) -> Maybe (Term n)
chk n x (Var y) = liftM Var (thick n x y)
chk n x (Leaf) = Just Leaf
chk n x (Fork s t) = liftM2 Fork(chk n x s)(chk n x t)
for :: Nat' n -> Term n -> Fin (S n) -> Fin (S n) -> Term n
for n t' x y = case thick n x y of
               Just y' -> Var y'
               Nothing -> t'

-----------------------------------------------------
-- substitution lists
data AList:: Nat ~> Nat ~> *0 where
  Anil:: AList n n
  Asnoc:: AList m' n -> Term m' -> Fin (S m') -> AList (S m') n
 deriving LeftRecord(a)

sub :: Nat' m -> AList m n -> (Fin m -> Term n)
sub _ (Anil) = Var
sub (S n) (Asnoc s t x) = compose(sub n s)(for n t x)
cat :: AList m n -> AList l m -> AList l n
cat xs (Anil) = xs
cat xs (Asnoc ys t x) = Asnoc (cat xs ys) t x

data SomeSub m = exists n . SomeSub (Nat' n) (AList m n)
asnoc :: SomeSub m -> Term m -> Fin (S m) -> SomeSub (S m)
asnoc (SomeSub m s) t x = SomeSub m (Asnoc s t x)

-----------------------------------------------------
-- unification
mgu :: Nat' m -> Term m -> Term m -> Maybe (SomeSub m)
mgu m s t = amgu m s t (SomeSub m Anil)

amgu :: Nat' m -> Term m -> Term m -> SomeSub m -> Maybe (SomeSub m)
amgu m (Leaf) (Leaf) acc = Just acc
amgu m (Leaf) (Fork s t) acc = Nothing
amgu m (Fork s t) (Leaf) acc = Nothing
amgu m (Fork s1 t1) (Fork s2 t2) acc =
     do { acc <- amgu m s1 t1 acc; amgu m s2 t2 acc }
amgu m (Var x) (Var y) (SomeSub _ Anil) = Just (flexFlex m x y)
amgu m (Var x) t (SomeSub _ Anil) = flexRigid m x t
amgu m s (Var x) (SomeSub _ Anil) = flexRigid m x s
amgu (S m) s t (SomeSub n (Asnoc sub r z)) =
     do sub <- amgu m (subst (for m r z) s) (subst (for m r z) t) (SomeSub n sub)
        return (asnoc sub r z)

flexFlex :: Nat' m -> Fin m -> Fin m -> SomeSub m
flexFlex (S m) x y = case thick m x y of
                     Just y' -> SomeSub m (Asnoc Anil (Var y') x)
                     Nothing -> SomeSub (S m) Anil

flexRigid :: Nat' m -> Fin m -> Term m -> Maybe (SomeSub m)
flexRigid (S m) x t = do
                      t' <- chk m x t
                      return (SomeSub m (Asnoc Anil t' x))

-- This extended example is an Omega port of Conor McBride's First Order Unification by Structural Recursion
-- From the paper "GADTs + Extensible Kinds = Dependent Programming"

-------- MAYBE MONAD --------------------------------
return x = Just x
fail s = Nothing
bind Nothing g = Nothing
bind (Just x) g = g x
liftM :: (a -> b) -> Maybe a -> Maybe b
liftM2 :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
liftM f ma = do { a <- ma; return (f a) }
liftM2 f ma mb = do { a <- ma; b <- mb; return (f a b) }

------- FINITE (VARIABLE) SETS & TERMS --------------
data Fin:: Nat ~> *0 where
   Fz:: Fin (S m)
   Fs:: Fin m -> Fin (S m)

data Term n = Var(Fin n) | Leaf | Fork(Term n)(Term n)

------- SUBSTITUTIONS -------------------------------
rename2sub :: (Fin m -> Fin n) -> Fin m -> Term n
rename2sub f i = Var (f i)
subst :: (Fin m -> Term n) -> Term m -> Term n
subst sub (Var x) = sub x
subst sub Leaf = Leaf
subst sub (Fork s t) = Fork(subst sub s)(subst sub t)
compose :: (Fin m -> Term n) -> (Fin l -> Term m) -> (Fin l -> Term n)
compose f g i = subst f (g i)

------- OCCURS CHECK --------------------------------
thick :: Nat' n -> Fin (S n) -> Fin (S n) -> Maybe (Fin n)
thick n (Fz) (Fz) = Nothing
thick n (Fz) (Fs y) = Just y
thick (S n) (Fs x) (Fz) = Just Fz
thick (S n) (Fs x) (Fs y) = liftM Fs (thick n x y)

chk :: Nat' n -> Fin (S n) -> Term (S n) -> Maybe (Term n)
chk n x (Var y) = liftM Var (thick n x y)
chk n x (Leaf) = Just Leaf
chk n x (Fork s t) = liftM2 Fork(chk n x s)(chk n x t)
for :: Nat' n -> Term n -> Fin (S n) -> Fin (S n) -> Term n
for n t' x y = case thick n x y of
               Just y' -> Var y'
               Nothing -> t'

-----------------------------------------------------
-- substitution lists
data AList:: Nat ~> Nat ~> *0 where
   Anil:: AList n n
   Asnoc:: AList m' n -> Term m' -> Fin (S m') -> AList (S m') n

sub :: Nat' m -> AList m n -> (Fin m -> Term n)
sub _ (Anil) = Var
sub (S n) (Asnoc s t x) = compose(sub n s)(for n t x)
cat :: AList m n -> AList l m -> AList l n
cat xs (Anil) = xs
cat xs (Asnoc ys t x) = Asnoc (cat xs ys) t x

data SomeSub m = exists n . SomeSub (Nat' n) (AList m n)
asnoc :: SomeSub m -> Term m -> Fin (S m) -> SomeSub (S m)
asnoc (SomeSub m s) t x = SomeSub m (Asnoc s t x)

-----------------------------------------------------
-- unification
mgu :: Nat' m -> Term m -> Term m -> Maybe (SomeSub m)
mgu m s t = amgu m s t (SomeSub m Anil)

amgu :: Nat' m -> Term m -> Term m -> SomeSub m -> Maybe (SomeSub m)
amgu m (Leaf) (Leaf) acc = Just acc
amgu m (Leaf) (Fork s t) acc = Nothing
amgu m (Fork s t) (Leaf) acc = Nothing
amgu m (Fork s1 t1) (Fork s2 t2) acc =
     do { acc <- amgu m s1 t1 acc; amgu m s2 t2 acc }
amgu m (Var x) (Var y) (SomeSub _ Anil) = Just (flexFlex m x y)
amgu m (Var x) t (SomeSub _ Anil) = flexRigid m x t
amgu m s (Var x) (SomeSub _ Anil) = flexRigid m x s
amgu (S m) s t (SomeSub n (Asnoc sub r z)) =
     do sub <- amgu m (subst (for m r z) s) (subst (for m r z) t) (SomeSub n sub)
        return (asnoc sub r z)

flexFlex :: Nat' m -> Fin m -> Fin m -> SomeSub m
flexFlex (S m) x y = case thick m x y of
                     Just y' -> SomeSub m (Asnoc Anil (Var y') x)
                     Nothing -> SomeSub (S m) Anil

flexRigid :: Nat' m -> Fin m -> Term m -> Maybe (SomeSub m)
flexRigid (S m) x t = do
                      t' <- chk m x t
                      return (SomeSub m (Asnoc Anil t' x))


remove unnecessary quantifiers

remove unnecessary quantifiers

data List :: a ~> *1 where
  Nil  :: List a
  Cons :: a ~> List a ~> List a

data LIST :: n ~> List a ~> *0 where
  NIL  :: LIST Z Nil
  CONS :: a -> LIST n l -> LIST (S n) (Cons a l)

repl :: Nat ~> a ~>  List a
{repl Z x} = Nil
{repl (S n) x} = Cons x {repl n x}

length :: LIST n l -> Nat' n
length NIL = Z
length (CONS x xs) = S (length xs)

lemFromLIST :: Nat' n -> Equal (Cons a as) {repl (S n) a} -> Equal as {repl n a}
lemFromLIST Z Eq = Eq
lemFromLIST (S n) Eq = case lemFromLIST n Eq of Eq -> Eq

lemFromLIST2 :: Nat' n -> Equal a b -> Equal {repl n a} {repl n b}
lemFromLIST2 Z Eq = Eq
lemFromLIST2 (S n) Eq = case lemFromLIST2 n Eq of Eq -> Eq

fromLIST :: LIST n {repl n a} -> [a]
fromLIST NIL = []
fromLIST (CONS x xs) =  (x : fromLIST xs)
 where
  lem = lemFromLIST (length xs)
  lem2 = lemFromLIST2 (length xs)
  theorem lem
  theorem lem2

data List :: forall a . a ~> *1 where
  Nil  :: List a
  Cons :: a ~> List a ~> List a

data LIST :: forall a n . n ~> List a ~> *0 where
  NIL  :: LIST Z Nil
  CONS :: a -> LIST n l -> LIST (S n) (Cons a l)

repl :: forall a (n::Nat) . Nat ~> a ~>  List a
{repl Z x} = Nil
{repl (S n) x} = Cons x {repl n x}

length :: LIST n l -> Nat' n
length NIL = Z
length (CONS x xs) = S (length xs)

lemFromLIST :: Nat' n -> Equal (Cons a as) {repl (S n) a} -> Equal as {repl n a}
lemFromLIST Z Eq = Eq
lemFromLIST (S n) Eq = case lemFromLIST n Eq of Eq -> Eq

lemFromLIST2 :: Nat' n -> Equal a b -> Equal {repl n a} {repl n b}
lemFromLIST2 Z Eq = Eq
lemFromLIST2 (S n) Eq = case lemFromLIST2 n Eq of Eq -> Eq

fromLIST :: LIST n {repl n a} -> [a]
fromLIST NIL = []
fromLIST (CONS x xs) =  (x : fromLIST xs)
 where
  lem = lemFromLIST (length xs)
  lem2 = lemFromLIST2 (length xs)
  theorem lem
  theorem lem2


[Tests] Updated comment

[Tests] Updated comment

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System
USING System.Collections.Generic
USING System.Linq
USING System.Text
USING XUnit

// WinBool test
BEGIN NAMESPACE XSharp.VO.Tests

	CLASS MacroTests

		[Fact, Trait("Category", "Macro")];
		METHOD Tests AS VOID
			LOCAL cMacro AS STRING
			LOCAL bMacro AS USUAL
			LOCAL uValue AS USUAL
			cMacro := "1+1"
			uValue := &(cMacro)
			Assert.Equal (2, (INT) uValue)
			cMacro := "{||1+2}"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro)
			Assert.Equal (3, (INT) uValue)
			cMacro := "{||SQrt(25)}"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro)
			Assert.Equal (5, (INT) uValue)
			cMacro := e"{|aaa,bbb| \"aaa\" + \"bbb\" }"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro,20,22)
			Assert.Equal ("aaabbb", (STRING) uValue)
			cMacro := "{|aaa,bbb| aaa + bbb }"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro,20,22)
			Assert.Equal (42, (INT) uValue)

            cMacro := "{| a, b, c |  'a' $ 'a|b|c|d' }"
            //cMacro := "{| a,b,c| c == a + b}"
            bMacro := MCompile(cMacro,TRUE)
			uValue := Eval(bMacro,1,2,3)
            Assert.Equal (TRUE, (LOGIC) uValue)
			cMacro := "{|aaa , bbb| aaa + bbb }"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro,20,22)
			Assert.Equal (42, (INT) uValue)
		    cMacro := "{|aaa , bbb| aaa + bbb + ccc }" // not existing field
			bMacro := MCompile(cMacro)
	        Assert.ThrowsAny<Exception>( { => Eval(bMacro,20,22) })
            RETURN

		[Fact, Trait("Category", "Macro")];
		METHOD FunctionOverloadTest AS VOID
			LOCAL cMacro AS STRING
			LOCAL bMacro AS USUAL
		    cMacro := "Left('abcdefg',2)" // should call my replacement function
			bMacro := MCompile(cMacro)

            Assert.Equal("MyLeft", eval(bMacro))

            RETURN

	END CLASS
END NAMESPACE // XSharp.Runtime.Tests

FUNCTION Left(cValue as STRING, nLen as DWORD) AS STRING
    RETURN "MyLeft"

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System
USING System.Collections.Generic
USING System.Linq
USING System.Text
USING XUnit

// WinBool test
BEGIN NAMESPACE XSharp.VO.Tests

	CLASS MacroTests

		[Fact, Trait("Category", "Macro")];
		METHOD Tests AS VOID
			LOCAL cMacro AS STRING
			LOCAL bMacro AS USUAL
			LOCAL uValue AS USUAL
			cMacro := "1+1"
			uValue := &(cMacro)
			Assert.Equal (2, (INT) uValue)
			cMacro := "{||1+2}"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro)
			Assert.Equal (3, (INT) uValue)
			cMacro := "{||SQrt(25)}"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro)
			Assert.Equal (5, (INT) uValue)
			cMacro := e"{|aaa,bbb| \"aaa\" + \"bbb\" }"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro,20,22)
			Assert.Equal ("aaabbb", (STRING) uValue)
			cMacro := "{|aaa,bbb| aaa + bbb }"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro,20,22)
			Assert.Equal (42, (INT) uValue)

            cMacro := "{| a, b, c |  'a' $ 'a|b|c|d' }"
            //cMacro := "{| a,b,c| c == a + b}"
            bMacro := MCompile(cMacro,TRUE)
			uValue := Eval(bMacro,1,2,3)
            Assert.Equal (TRUE, (LOGIC) uValue)
			cMacro := "{|aaa , bbb| aaa + bbb }"
			bMacro := MCompile(cMacro)
			uValue := Eval(bMacro,20,22)
			Assert.Equal (42, (INT) uValue)
		    cMacro := "{|aaa , bbb| aaa + bbb + ccc }" // not existing field
			bMacro := MCompile(cMacro)
	        Assert.ThrowsAny<Exception>( { => Eval(bMacro,20,22) })
            RETURN

		[Fact, Trait("Category", "Macro")];
		METHOD FunctionOverloadTest AS VOID
			LOCAL cMacro AS STRING
			LOCAL bMacro AS USUAL
		    cMacro := "Left('abcdefg',2)" // not existing field
			bMacro := MCompile(cMacro)

            Assert.Equal("MyLeft", eval(bMacro))

            RETURN

	END CLASS
END NAMESPACE // XSharp.Runtime.Tests

FUNCTION Left(cValue as STRING, nLen as DWORD) AS STRING
    RETURN "MyLeft"


[Runtime] Fixed ambigous method call

[Runtime] Fixed ambigous method call

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//

// Please note that the VFP Docs speak about "Statement Handles" for connections and also
// about "statement handles" for statements. These are returned for example by SqlPrepare
// In this code we speak about "connection handles" and "statement handles".

USING System
USING System.Collections.Generic
USING System.Text
USING XSharp.Data
USING System.Data.Common
USING XSharp.VFP
USING XSharp.RDD
USING System.Data

INTERNAL CLASS XSharp.VFP.HandleCacheElement
    INTERNAL Number     AS LONG
    INTERNAL @@Value    AS SQLStatement
END CLASS

INTERNAL STATIC CLASS XSharp.VFP.SQLSupport
    STATIC PRIVATE INITONLY Cache AS Dictionary <LONG, HandleCacheElement>
    STATIC INTERNAL UniqueId AS LONG
    STATIC INTERNAL Factory AS ISqlFactory
    STATIC PRIVATE DefaultValues AS Dictionary<SQLProperty, OBJECT>

    STATIC INTERNAL PROPERTY DispLogin AS LONG GET SQLSupport.GetDefault<LONG>(SQLProperty.DispLogin)

    STATIC CONSTRUCTOR
        Cache    := Dictionary<LONG, HandleCacheElement>{}
        UniqueId := 0
        Factory  := XSharp.Data.Functions.GetSqlFactory()
        DefaultValues := Dictionary<SQLProperty, OBJECT>{}
        DefaultValues.Add(SQLProperty.Asynchronous       ,FALSE)
        DefaultValues.Add(SQLProperty.BatchMode          ,TRUE)
        DefaultValues.Add(SQLProperty.ConnectBusy        ,FALSE)
        DefaultValues.Add(SQLProperty.ConnectString      ,"")
        DefaultValues.Add(SQLProperty.ConnectTimeOut     ,15)
        DefaultValues.Add(SQLProperty.DataSource         ,"")
        DefaultValues.Add(SQLProperty.DisconnectRollback ,FALSE)
        DefaultValues.Add(SQLProperty.DispLogin          ,DB_PROMPTCOMPLETE)
        DefaultValues.Add(SQLProperty.DispWarnings       ,FALSE)
        DefaultValues.Add(SQLProperty.IdleTimeout        ,0)
        //DefaultValues.Add(SQLProperty.ODBChdbc           ,NULL)
        //DefaultValues.Add(SQLProperty.ODBChstmt          ,NULL)
        DefaultValues.Add(SQLProperty.PacketSize         ,4096)
        //DefaultValues.Add(SQLProperty.Password           ,"")
        DefaultValues.Add(SQLProperty.QueryTimeOut       ,0)
        //DefaultValues.Add(SQLProperty.Shared             ,
        DefaultValues.Add(SQLProperty.Transactions       ,DB_TRANSAUTO)
        //DefaultValues.Add(SQLProperty.UserId             ,"")
        DefaultValues.Add(SQLProperty.WaitTime           ,100)

    STATIC METHOD GetDefault<T>(nDef AS SQLProperty) AS T
        IF DefaultValues:ContainsKey(nDef)
            RETURN (T) DefaultValues[nDef]
        ENDIF
        RETURN DEFAULT (T)

    STATIC METHOD FindStatement(nId AS LONG) AS OBJECT
        IF Cache:ContainsKey(nId)
            VAR element := Cache[nId]
            RETURN element:Value
        ENDIF
        RETURN NULL

    STATIC METHOD GetStatements() AS IList<SQLStatement>
        LOCAL aResult := List<SQLStatement>{} AS IList<SQLStatement>
        FOREACH VAR oElement IN Cache:Values
            aResult:Add(oElement:Value)
        NEXT
        RETURN aResult

    STATIC METHOD AddStatement(oStmt AS SQLStatement) AS LONG
        VAR element := HandleCacheElement{}
        element:Number := ++UniqueId
        element:Value  := oStmt
        Cache:Add(element:Number, element)
        oStmt:Handle := element:Number
        RETURN element:Number

    STATIC METHOD RemoveStatement(nId AS LONG) AS LOGIC
        IF Cache:ContainsKey(nId)
            VAR element := Cache[nId]
            Cache:Remove(nId)
            IF element:Value IS IDisposable VAR disp
                disp:Dispose()
            ENDIF
            RETURN TRUE
        ENDIF
        RETURN FALSE

    STATIC METHOD GetSetProperty(nHandle AS LONG, cProperty AS STRING, newValue := NULL AS OBJECT) AS OBJECT
        LOCAL oStmt AS XSharp.VFP.SQLStatement
        LOCAL result := NULL AS OBJECT
        LOCAL cNewProp AS STRING
        cNewProp := GetPartialEnumName(cProperty, typeof(SQLProperty))
        IF String.IsNullOrEmpty(cNewProp)
            VAR cMessage := String.Format(__VfpStr(VFPErrors.PROPERTY_UNKNOWN), cProperty)
            THROW Error{cMessage}
        ENDIF
        var nProperty := (SQLProperty) GetSQLProperty(cNewProp)
        IF nHandle == 0 // Default values
            IF DefaultValues:ContainsKey(nProperty)
                result := DefaultValues[nProperty]
                IF newValue != NULL
                    DefaultValues[nProperty] := newValue
                    result := 1
                ENDIF
            ENDIF
            RETURN result
        ENDIF
        oStmt := FindStatement(nHandle)
        IF oStmt == NULL_OBJECT
            RETURN -1
        ENDIF
        SWITCH nProperty
           CASE SQLProperty.Asynchronous
                // Specifies whether result sets are returned synchronously (FALSE (.F.), the DEFAULT), or asynchronously (TRUE (.T.)). Read/write.
               result := oStmt:Asynchronous
               IF newValue IS LOGIC VAR newVal
                    oStmt:Asynchronous := newVal
                    result := 1
               ENDIF

           CASE SQLProperty.BatchMode
                //  Specifies whether SQLEXEC( ) returns result sets all at once (TRUE (.T.), the DEFAULT), or individually WITH SQLMORERESULTS( ) (FALSE (.F.)). Read/write.
                result := oStmt:BatchMode
                IF newValue IS LOGIC VAR newVal
                    oStmt:BatchMode := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.ConnectBusy
                //  Contains TRUE (.T.) IF a shared connection IS busy; OTHERWISE contains FALSE (.F.). Read-only.
                result := oStmt:ConnectBusy
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.ConnectString
                // The login connection string. Read-only.
                result := oStmt:ConnectionString
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.ConnectTimeOut
                result := oStmt:ConnectionTimeOut
                IF newValue IS LONG VAR newVal
                    oStmt:ConnectionTimeOut := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.DataSource
                result := oStmt:DataSource
                IF newValue IS STRING VAR newVal
                    oStmt:DataSource := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.DisconnectRollback
              result := oStmt:DisconnectRollback
               IF newValue IS LOGIC VAR newVal
                    oStmt:DisconnectRollback := newVal
                    result := 1
               ENDIF

           CASE SQLProperty.DispLogin // Contains a numeric value that determines when the ODBC Login dialog box IS displayed. DispLogin may assume the following values:
                result := SQLSupport.DispLogin

           CASE SQLProperty.DispWarnings
                result := oStmt:DispWarnings
                IF newValue IS LOGIC VAR newVal
                   oStmt:DispWarnings := newVal
                    result := 1
                ENDIF
            CASE SQLProperty.IdleTimeout
                result := oStmt:IdleTimeout
                IF newValue IS LONG VAR newVal
                   oStmt:IdleTimeout := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.NativeCommand
                result := oStmt:ODBChstmt:CommandText

           CASE SQLProperty.ODBChdbc
                result := oStmt:ODBChdbc
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.ODBChstmt
                result := oStmt:ODBChstmt
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF
           CASE SQLProperty.PacketSize

               result := oStmt:PacketSize
                IF newValue IS LONG VAR newVal
                   oStmt:PacketSize := newVal
                    result := 1
                ENDIF

          CASE SQLProperty.Password
                result := oStmt:Password
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.QueryTimeOut
                result := oStmt:QueryTimeOut
                IF newValue IS LONG VAR newVal
                   oStmt:QueryTimeOut := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.Shared
                result := oStmt:Shared
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.Transactions
                result := oStmt:TransactionMode
                IF newValue IS LONG VAR newVal
                    IF newVal == DB_TRANSAUTO .OR. newVal == DB_TRANSMANUAL
                        oStmt:TransactionMode := newVal
                        result := 1
                    ENDIF
                ENDIF

           CASE SQLProperty.UserId
                result := oStmt:UserId
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.WaitTime
                result := oStmt:WaitTime
                IF newValue IS LONG VAR newVal
                   oStmt:WaitTime := newVal
                    result := 1
                ENDIF
        OTHERWISE
            result := -1
        END SWITCH
        RETURN result

    // This method is located here so we can keep the  a table with oldname - newname pairs, so we do not have
    // to do this over and over again.

END CLASS

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//

// Please note that the VFP Docs speak about "Statement Handles" for connections and also
// about "statement handles" for statements. These are returned for example by SqlPrepare
// In this code we speak about "connection handles" and "statement handles".

USING System
USING System.Collections.Generic
USING System.Text
USING XSharp.Data
USING System.Data.Common
USING XSharp.VFP
USING XSharp.RDD
USING System.Data

INTERNAL CLASS XSharp.VFP.HandleCacheElement
    INTERNAL Number     AS LONG
    INTERNAL @@Value    AS SQLStatement
END CLASS

INTERNAL STATIC CLASS XSharp.VFP.SQLSupport
    STATIC PRIVATE INITONLY Cache AS Dictionary <LONG, HandleCacheElement>
    STATIC INTERNAL UniqueId AS LONG
    STATIC INTERNAL Factory AS ISqlFactory
    STATIC PRIVATE DefaultValues AS Dictionary<SQLProperty, OBJECT>

    STATIC INTERNAL PROPERTY DispLogin AS LONG GET GetDefault<LONG>(SQLProperty.DispLogin)

    STATIC CONSTRUCTOR
        Cache    := Dictionary<LONG, HandleCacheElement>{}
        UniqueId := 0
        Factory  := XSharp.Data.Functions.GetSqlFactory()
        DefaultValues := Dictionary<SQLProperty, OBJECT>{}
        DefaultValues.Add(SQLProperty.Asynchronous       ,FALSE)
        DefaultValues.Add(SQLProperty.BatchMode          ,TRUE)
        DefaultValues.Add(SQLProperty.ConnectBusy        ,FALSE)
        DefaultValues.Add(SQLProperty.ConnectString      ,"")
        DefaultValues.Add(SQLProperty.ConnectTimeOut     ,15)
        DefaultValues.Add(SQLProperty.DataSource         ,"")
        DefaultValues.Add(SQLProperty.DisconnectRollback ,FALSE)
        DefaultValues.Add(SQLProperty.DispLogin          ,DB_PROMPTCOMPLETE)
        DefaultValues.Add(SQLProperty.DispWarnings       ,FALSE)
        DefaultValues.Add(SQLProperty.IdleTimeout        ,0)
        //DefaultValues.Add(SQLProperty.ODBChdbc           ,NULL)
        //DefaultValues.Add(SQLProperty.ODBChstmt          ,NULL)
        DefaultValues.Add(SQLProperty.PacketSize         ,4096)
        //DefaultValues.Add(SQLProperty.Password           ,"")
        DefaultValues.Add(SQLProperty.QueryTimeOut       ,0)
        //DefaultValues.Add(SQLProperty.Shared             ,
        DefaultValues.Add(SQLProperty.Transactions       ,DB_TRANSAUTO)
        //DefaultValues.Add(SQLProperty.UserId             ,"")
        DefaultValues.Add(SQLProperty.WaitTime           ,100)

    STATIC METHOD GetDefault<T>(nDef AS SQLProperty) AS T
        IF DefaultValues:ContainsKey(nDef)
            RETURN (T) DefaultValues[nDef]
        ENDIF
        RETURN DEFAULT (T)

    STATIC METHOD FindStatement(nId AS LONG) AS OBJECT
        IF Cache:ContainsKey(nId)
            VAR element := Cache[nId]
            RETURN element:Value
        ENDIF
        RETURN NULL

    STATIC METHOD GetStatements() AS IList<SQLStatement>
        LOCAL aResult := List<SQLStatement>{} AS IList<SQLStatement>
        FOREACH VAR oElement IN Cache:Values
            aResult:Add(oElement:Value)
        NEXT
        RETURN aResult

    STATIC METHOD AddStatement(oStmt AS SQLStatement) AS LONG
        VAR element := HandleCacheElement{}
        element:Number := ++UniqueId
        element:Value  := oStmt
        Cache:Add(element:Number, element)
        oStmt:Handle := element:Number
        RETURN element:Number

    STATIC METHOD RemoveStatement(nId AS LONG) AS LOGIC
        IF Cache:ContainsKey(nId)
            VAR element := Cache[nId]
            Cache:Remove(nId)
            IF element:Value IS IDisposable VAR disp
                disp:Dispose()
            ENDIF
            RETURN TRUE
        ENDIF
        RETURN FALSE

    STATIC METHOD GetSetProperty(nHandle AS LONG, cProperty AS STRING, newValue := NULL AS OBJECT) AS OBJECT
        LOCAL oStmt AS XSharp.VFP.SQLStatement
        LOCAL result := NULL AS OBJECT
        LOCAL cNewProp AS STRING
        cNewProp := GetPartialEnumName(cProperty, typeof(SQLProperty))
        IF String.IsNullOrEmpty(cNewProp)
            VAR cMessage := String.Format(__VfpStr(VFPErrors.PROPERTY_UNKNOWN), cProperty)
            THROW Error{cMessage}
        ENDIF
        var nProperty := (SQLProperty) GetSQLProperty(cNewProp)
        IF nHandle == 0 // Default values
            IF DefaultValues:ContainsKey(nProperty)
                result := DefaultValues[nProperty]
                IF newValue != NULL
                    DefaultValues[nProperty] := newValue
                    result := 1
                ENDIF
            ENDIF
            RETURN result
        ENDIF
        oStmt := FindStatement(nHandle)
        IF oStmt == NULL_OBJECT
            RETURN -1
        ENDIF
        SWITCH nProperty
           CASE SQLProperty.Asynchronous
                // Specifies whether result sets are returned synchronously (FALSE (.F.), the DEFAULT), or asynchronously (TRUE (.T.)). Read/write.
               result := oStmt:Asynchronous
               IF newValue IS LOGIC VAR newVal
                    oStmt:Asynchronous := newVal
                    result := 1
               ENDIF

           CASE SQLProperty.BatchMode
                //  Specifies whether SQLEXEC( ) returns result sets all at once (TRUE (.T.), the DEFAULT), or individually WITH SQLMORERESULTS( ) (FALSE (.F.)). Read/write.
                result := oStmt:BatchMode
                IF newValue IS LOGIC VAR newVal
                    oStmt:BatchMode := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.ConnectBusy
                //  Contains TRUE (.T.) IF a shared connection IS busy; OTHERWISE contains FALSE (.F.). Read-only.
                result := oStmt:ConnectBusy
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.ConnectString
                // The login connection string. Read-only.
                result := oStmt:ConnectionString
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.ConnectTimeOut
                result := oStmt:ConnectionTimeOut
                IF newValue IS LONG VAR newVal
                    oStmt:ConnectionTimeOut := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.DataSource
                result := oStmt:DataSource
                IF newValue IS STRING VAR newVal
                    oStmt:DataSource := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.DisconnectRollback
              result := oStmt:DisconnectRollback
               IF newValue IS LOGIC VAR newVal
                    oStmt:DisconnectRollback := newVal
                    result := 1
               ENDIF

           CASE SQLProperty.DispLogin // Contains a numeric value that determines when the ODBC Login dialog box IS displayed. DispLogin may assume the following values:
                result := SQLSupport.DispLogin

           CASE SQLProperty.DispWarnings
                result := oStmt:DispWarnings
                IF newValue IS LOGIC VAR newVal
                   oStmt:DispWarnings := newVal
                    result := 1
                ENDIF
            CASE SQLProperty.IdleTimeout
                result := oStmt:IdleTimeout
                IF newValue IS LONG VAR newVal
                   oStmt:IdleTimeout := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.NativeCommand
                result := oStmt:ODBChstmt:CommandText

           CASE SQLProperty.ODBChdbc
                result := oStmt:ODBChdbc
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.ODBChstmt
                result := oStmt:ODBChstmt
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF
           CASE SQLProperty.PacketSize

               result := oStmt:PacketSize
                IF newValue IS LONG VAR newVal
                   oStmt:PacketSize := newVal
                    result := 1
                ENDIF

          CASE SQLProperty.Password
                result := oStmt:Password
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.QueryTimeOut
                result := oStmt:QueryTimeOut
                IF newValue IS LONG VAR newVal
                   oStmt:QueryTimeOut := newVal
                    result := 1
                ENDIF

           CASE SQLProperty.Shared
                result := oStmt:Shared
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.Transactions
                result := oStmt:TransactionMode
                IF newValue IS LONG VAR newVal
                    IF newVal == DB_TRANSAUTO .OR. newVal == DB_TRANSMANUAL
                        oStmt:TransactionMode := newVal
                        result := 1
                    ENDIF
                ENDIF

           CASE SQLProperty.UserId
                result := oStmt:UserId
                IF newValue != NULL    // Readonly
                    result := -1
                ENDIF

           CASE SQLProperty.WaitTime
                result := oStmt:WaitTime
                IF newValue IS LONG VAR newVal
                   oStmt:WaitTime := newVal
                    result := 1
                ENDIF
        OTHERWISE
            result := -1
        END SWITCH
        RETURN result

    // This method is located here so we can keep the  a table with oldname - newname pairs, so we do not have
    // to do this over and over again.

END CLASS