diff --git a/src/Runtime/XSharp.VFP.Tests/FoxArraytests.prg b/src/Runtime/XSharp.VFP.Tests/FoxArraytests.prg index 353acde943..04906a9589 100644 --- a/src/Runtime/XSharp.VFP.Tests/FoxArraytests.prg +++ b/src/Runtime/XSharp.VFP.Tests/FoxArraytests.prg @@ -348,4 +348,117 @@ BEGIN NAMESPACE XSharp.VFP.Tests Assert.Equal(100U, ALen(arr,1)) Assert.Equal(0U, ALen(arr,2)) END CLASS + + CLASS ALinesTests + [Fact, Trait("Category", "ALines")]; + METHOD BasicParsingTest() AS VOID + DIMENSION aTest[1] + LOCAL cText := e"Line1\r\nLine2\r\nLine3\r\nLine4" AS STRING + + VAR nCount := aLines(aTest, cText) + + Assert.Equal(4, (INT)nCount) + Assert.Equal(4, (INT)ALen(aTest)) + Assert.Equal("Line1", aTest[1]) + Assert.Equal("Line2", aTest[2]) + Assert.Equal("Line3", aTest[3]) + Assert.Equal("Line4", aTest[4]) + END METHOD + + [Fact, Trait("Category", "ALines")]; + METHOD CustomSeparatorTest() AS VOID + DIMENSION aTest[1] + VAR nCount := aLines(aTest, "A,B,C", 0, ",") + + Assert.Equal(3, (INT)nCount) + Assert.Equal("B", aTest[2]) + END METHOD + + [Fact, Trait("Category", "ALines")]; + METHOD MultiSeparatorTest() AS VOID + DIMENSION aTest[1] + VAR nCount := aLines(aTest, "One.Two,Three;Four", 0, ".", ",", ";") + + Assert.Equal(4, (INT)nCount) + Assert.Equal("Two", aTest[2]) + Assert.Equal("Three", aTest[3]) + END METHOD + + + [Fact, Trait("Category", "ALines")]; + METHOD FlagTrimTest() AS VOID + DIMENSION aTest[1] + VAR cText := " Item1 , Item2 " + + && Without Trim + aLines(aTest, cText, 0, ",") + Assert.Equal(" Item1 ", aTest[1]) + + && With Trim + aLines(aTest, cText, 1, ",") + Assert.Equal("Item1", aTest[1]) + Assert.Equal("Item2", aTest[2]) + END METHOD + + [Fact, Trait("Category", "ALines")]; + METHOD FlagNoEmptyTest() AS VOID + DIMENSION aTest[1] + VAR cText := "A,,B,,,C" + + && Without flag (must include empty lines) + VAR nAll := aLines(aTest, cText, 0, ",") + Assert.Equal(6, (INT)nAll) + + && With flag (just content) + VAR nNoEmpty := aLines(aTest, cText, 4, ",") + Assert.Equal(3, (INT)nNoEmpty) + Assert.Equal("B", aTest[2]) + Assert.Equal("C", aTest[3]) + END METHOD + + [Fact, Trait("Category", "ALines")]; + METHOD FlagCaseInsensitiveTest() AS VOID + DIMENSION aTest[1] + VAR cText := "User
Name
Age" + + && No flag (case sensitive) -> do not cut
+ VAR nSensitive := aLines(aTest, cText, 0, "
") + Assert.Equal(2, (INT)nSensitive) + + && With flag (case insensitive) -> cut
+ VAR nInsensitive := aLines(aTest, cText, 8, "
") + Assert.Equal(3, (INT)nInsensitive) + Assert.Equal("Age", aTest[3]) + END METHOD + + [Fact, Trait("Category", "ALines")]; + METHOD FlagIncludeSeparatorTest() AS VOID + DIMENSION aTest[1] + VAR cText := "A,B" + + VAR nCount := aLines(aTest, cText, 16, ",") + Assert.Equal(3, (INT)nCount) + Assert.Equal("A", aTest[1]) + Assert.Equal(",", aTest[2]) + Assert.Equal("B", aTest[3]) + END METHOD + + [Fact, Trait("Category", "ALines")]; + METHOD FlagIncludeLastTest() AS VOID + DIMENSION aTest[1] + VAR cText := "A,B," + + && Case 1: without flag (default) -> "A", "B" (ignore last empty) + VAR nCount1 := aLines(aTest, cText, 0, ",") + Assert.Equal(2, (INT)nCount1) + Assert.Equal("B", aTest[2]) + + && Case 2: with flag -> "A", "B", "" (include last empty) + VAR nCount2 := aLines(aTest, cText, 2, ",") + Assert.Equal(3, (INT)nCount2) + Assert.Equal("", aTest[3]) + + END METHOD + + END CLASS END NAMESPACE diff --git a/src/Runtime/XSharp.VFP/ArrayFunctions.prg b/src/Runtime/XSharp.VFP/ArrayFunctions.prg index ddf8163e49..9706c29b21 100644 --- a/src/Runtime/XSharp.VFP/ArrayFunctions.prg +++ b/src/Runtime/XSharp.VFP/ArrayFunctions.prg @@ -5,6 +5,9 @@ // USING XSharp.Internal +USING System.Text.RegularExpressions +USING System.Collections.Generic +USING System.Text INTERNAL FUNCTION FoxALen(a as ARRAY) AS DWORD @@ -233,4 +236,92 @@ RETURN END FUNCTION +/// +FUNCTION ALines (ArrayName AS ARRAY, cExpression AS STRING, nFlags := 0 AS INT, cParseChars PARAMS STRING[]) AS DWORD + IF cExpression == null + cExpression := "" + ENDIF + + VAR separators := List{} + + IF cParseChars == NULL OR cParseChars:Length == 0 + separators:Add(e"\r\n") + separators:Add(e"\r") + separators:Add(e"\n") + ELSE + separators:AddRange(cParseChars) + ENDIF + + LOCAL lTrim := (nFlags & ALINES_TRIM) != 0 AS LOGIC // 1 + LOCAL lIncludeLast := (nFlags & ALINES_INCLUDE_LAST) != 0 AS LOGIC // 2 + LOCAL lNoEmpty := (nFlags & ALINES_NO_EMPTY) != 0 AS LOGIC // 4 + LOCAL lIgnoreCase := (nFlags & ALINES_CASE_IGNORE) != 0 AS LOGIC // 8 + LOCAL lIncludeSep := (nFlags & ALINES_INCLUDE_SEP) != 0 AS LOGIC // 16 + + var sbPattern := StringBuilder{} + FOREACH VAR sep IN separators + IF sbPattern:Length > 0 + sbPattern:Append("|") + ENDIF + + VAR safeSep := Regex.Escape(sep) + IF lIncludeSep + sbPattern:Append("(" + safeSep + ")") + ELSE + sbPattern:Append("(?:" + safeSep + ")") + ENDIF + NEXT + + VAR regexOptions := RegexOptions.None + IF lIgnoreCase + regexOptions := RegexOptions.IgnoreCase + ENDIF + + VAR aRawParts := Regex.Split(cExpression, sbPattern:ToString(), regexOptions) + + VAR finalLines := List{} + VAR nIndex := 0 + + FOREACH VAR sLine IN aRawParts + VAR sTemp := sLine + VAR lIsSeparator := FALSE + IF lIncludeSep + lIsSeparator := (nIndex % 2) == 1 + ENDIF + + IF lTrim AND !lIsSeparator + sTemp := sTemp:Trim() + ENDIF + + IF lNoEmpty AND String.IsNullOrEmpty(sTemp) AND !lIsSeparator + nIndex++ + LOOP + ENDIF + + finalLines:Add(sTemp) + nIndex++ + NEXT + + IF !lIncludeLast AND !lNoEmpty AND finalLines:Count > 0 + VAR nLastIdx := finalLines:Count - 1 + IF String.IsNullOrEmpty(finalLines[nLastIdx]) + finalLines:RemoveAt(nLastIdx) + ENDIF + ENDIF + + VAR nRows := (DWORD)finalLines:Count + + IF nRows == 0 AND !lNoEmpty + nRows := 1 + finalLines:Add("") + ENDIF + + ASize(ArrayName, nRows) + + FOR VAR i := 0 TO (INT)nRows - 1 + ArrayName[i + 1] := finalLines[i] + NEXT + + RETURN nRows +END FUNCTION diff --git a/src/Runtime/XSharp.VFP/Defines.prg b/src/Runtime/XSharp.VFP/Defines.prg index f5fec7c0dd..fce31fe9bc 100644 --- a/src/Runtime/XSharp.VFP/Defines.prg +++ b/src/Runtime/XSharp.VFP/Defines.prg @@ -1490,3 +1490,10 @@ DEFINE STRCNV_UNIBE_SB := 18 DEFINE STRCNV_ID_LCID := 0 DEFINE STRCNV_ID_CODEPAGE := 1 DEFINE STRCNV_ID_CHARSET := 2 + +// ALINES Flags +DEFINE ALINES_TRIM := 1 +DEFINE ALINES_INCLUDE_LAST := 2 +DEFINE ALINES_NO_EMPTY := 4 +DEFINE ALINES_CASE_IGNORE := 8 +DEFINE ALINES_INCLUDE_SEP := 16 diff --git a/src/Runtime/XSharp.VFP/ToDo-A.prg b/src/Runtime/XSharp.VFP/ToDo-A.prg index b77527bbd9..cadbcc0282 100644 --- a/src/Runtime/XSharp.VFP/ToDo-A.prg +++ b/src/Runtime/XSharp.VFP/ToDo-A.prg @@ -27,12 +27,6 @@ FUNCTION AGetFileVersion (ArrayName, cFileName) THROW NotImplementedException{} //RETURN 0 -/// -- todo -- -/// -FUNCTION ALines (ArrayName, cExpression , nFlags , cParseChar , cParseChar2 ) - THROW NotImplementedException{} - //RETURN 0 - /// -- todo -- /// FUNCTION AMembers (ArrayName, oObjectNameOrClassName , nArrayContentsID , cFlags)