From 20f519d0d8570c3132b554150174b39fc46e336e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Melvyn=20La=C3=AFly?= Date: Sat, 4 Jun 2022 22:15:59 +0200 Subject: [PATCH 1/5] Fix tests --- .../FSharp.Data.DesignTime.Tests.fsproj | 1 + .../SignatureTestCases.config | 2 +- ...r.csv,,,True,False,False,,nb-NO,.expected} | 36 +++++++++---------- tests/FSharp.Data.Tests/CsvProvider.fs | 11 +++++- 4 files changed, 30 insertions(+), 20 deletions(-) rename tests/FSharp.Data.DesignTime.Tests/expected/{Csv,DnbHistoriskeKurser.csv,,,True,False,False,,fr-FR,.expected => Csv,DnbHistoriskeKurser.csv,,,True,False,False,,nb-NO,.expected} (97%) diff --git a/tests/FSharp.Data.DesignTime.Tests/FSharp.Data.DesignTime.Tests.fsproj b/tests/FSharp.Data.DesignTime.Tests/FSharp.Data.DesignTime.Tests.fsproj index 47c25d563..cbecb419b 100644 --- a/tests/FSharp.Data.DesignTime.Tests/FSharp.Data.DesignTime.Tests.fsproj +++ b/tests/FSharp.Data.DesignTime.Tests/FSharp.Data.DesignTime.Tests.fsproj @@ -14,6 +14,7 @@ + diff --git a/tests/FSharp.Data.DesignTime.Tests/SignatureTestCases.config b/tests/FSharp.Data.DesignTime.Tests/SignatureTestCases.config index 47a3ae15b..cb4260fed 100644 --- a/tests/FSharp.Data.DesignTime.Tests/SignatureTestCases.config +++ b/tests/FSharp.Data.DesignTime.Tests/SignatureTestCases.config @@ -1,7 +1,7 @@ Csv,SmallTest.csv,,,true,false,false,,, Csv,MSFT.csv,,,true,false,false,,, Csv,AirQuality.csv,;,,true,false,false,,, -Csv,DnbHistoriskeKurser.csv,,,true,false,false,,fr-FR, +Csv,DnbHistoriskeKurser.csv,,,true,false,false,,nb-NO, Csv,file with spaces.csv,,,true,false,false,,, Csv,LastFM.tsv,,,false,false,false,,, Csv,Titanic.csv,,,true,false,false,,, diff --git a/tests/FSharp.Data.DesignTime.Tests/expected/Csv,DnbHistoriskeKurser.csv,,,True,False,False,,fr-FR,.expected b/tests/FSharp.Data.DesignTime.Tests/expected/Csv,DnbHistoriskeKurser.csv,,,True,False,False,,nb-NO,.expected similarity index 97% rename from tests/FSharp.Data.DesignTime.Tests/expected/Csv,DnbHistoriskeKurser.csv,,,True,False,False,,fr-FR,.expected rename to tests/FSharp.Data.DesignTime.Tests/expected/Csv,DnbHistoriskeKurser.csv,,,True,False,False,,nb-NO,.expected index e721d4b15..0f5081482 100644 --- a/tests/FSharp.Data.DesignTime.Tests/expected/Csv,DnbHistoriskeKurser.csv,,,True,False,False,,fr-FR,.expected +++ b/tests/FSharp.Data.DesignTime.Tests/expected/Csv,DnbHistoriskeKurser.csv,,,True,False,False,,nb-NO,.expected @@ -1,7 +1,7 @@ class CsvProvider : FDR.CsvFile new : rows:CsvProvider+Row seq -> CsvProvider let rowToStringArray = new Func<_,_>(fun (row:_ * _ * _ * _ * _ * _ * _ * _) -> - [| TextRuntime.ConvertDateTimeBack("fr-FR", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) + [| TextRuntime.ConvertDateTimeBack("nb-NO", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) TextRuntime.ConvertStringBack(Some (let _,t2,_,_,_,_,_,_,_,_,_ = row in t2)) TextRuntime.ConvertStringBack(Some (let _,_,t3,_,_,_,_,_,_,_,_ = row in t3)) TextRuntime.ConvertStringBack(Some (let _,_,_,t4,_,_,_,_,_,_,_ = row in t4)) @@ -27,7 +27,7 @@ class CsvProvider : FDR.CsvFile new : () -> CsvProvider let stringArrayToRow = new Func<_,_,_>(fun (parent:obj) (row:string[]) -> let value = TextConversions.AsString(row.[0]) - TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("fr-FR", value), value), + TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("nb-NO", value), value), let value = TextConversions.AsString(row.[1]) TextRuntime.GetNonOptionalValue("USD", TextRuntime.ConvertString(value), value), let value = TextConversions.AsString(row.[2]) @@ -49,7 +49,7 @@ class CsvProvider : FDR.CsvFile let value = TextConversions.AsString(row.[10]) TextRuntime.GetNonOptionalValue("AUD", TextRuntime.ConvertString(value), value)) let rowToStringArray = new Func<_,_>(fun (row:_ * _ * _ * _ * _ * _ * _ * _) -> - [| TextRuntime.ConvertDateTimeBack("fr-FR", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) + [| TextRuntime.ConvertDateTimeBack("nb-NO", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) TextRuntime.ConvertStringBack(Some (let _,t2,_,_,_,_,_,_,_,_,_ = row in t2)) TextRuntime.ConvertStringBack(Some (let _,_,t3,_,_,_,_,_,_,_,_ = row in t3)) TextRuntime.ConvertStringBack(Some (let _,_,_,t4,_,_,_,_,_,_,_ = row in t4)) @@ -66,7 +66,7 @@ class CsvProvider : FDR.CsvFile let f = new Func<_,_>(fun (t:TextReader) -> let stringArrayToRow = new Func<_,_,_>(fun (parent:obj) (row:string[]) -> let value = TextConversions.AsString(row.[0]) - TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("fr-FR", value), value), + TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("nb-NO", value), value), let value = TextConversions.AsString(row.[1]) TextRuntime.GetNonOptionalValue("USD", TextRuntime.ConvertString(value), value), let value = TextConversions.AsString(row.[2]) @@ -88,7 +88,7 @@ class CsvProvider : FDR.CsvFile let value = TextConversions.AsString(row.[10]) TextRuntime.GetNonOptionalValue("AUD", TextRuntime.ConvertString(value), value)) let rowToStringArray = new Func<_,_>(fun (row:_ * _ * _ * _ * _ * _ * _ * _) -> - [| TextRuntime.ConvertDateTimeBack("fr-FR", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) + [| TextRuntime.ConvertDateTimeBack("nb-NO", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) TextRuntime.ConvertStringBack(Some (let _,t2,_,_,_,_,_,_,_,_,_ = row in t2)) TextRuntime.ConvertStringBack(Some (let _,_,t3,_,_,_,_,_,_,_,_ = row in t3)) TextRuntime.ConvertStringBack(Some (let _,_,_,t4,_,_,_,_,_,_,_ = row in t4)) @@ -106,7 +106,7 @@ class CsvProvider : FDR.CsvFile let f = new Func<_,_>(fun (t:TextReader) -> let stringArrayToRow = new Func<_,_,_>(fun (parent:obj) (row:string[]) -> let value = TextConversions.AsString(row.[0]) - TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("fr-FR", value), value), + TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("nb-NO", value), value), let value = TextConversions.AsString(row.[1]) TextRuntime.GetNonOptionalValue("USD", TextRuntime.ConvertString(value), value), let value = TextConversions.AsString(row.[2]) @@ -128,7 +128,7 @@ class CsvProvider : FDR.CsvFile let value = TextConversions.AsString(row.[10]) TextRuntime.GetNonOptionalValue("AUD", TextRuntime.ConvertString(value), value)) let rowToStringArray = new Func<_,_>(fun (row:_ * _ * _ * _ * _ * _ * _ * _) -> - [| TextRuntime.ConvertDateTimeBack("fr-FR", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) + [| TextRuntime.ConvertDateTimeBack("nb-NO", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) TextRuntime.ConvertStringBack(Some (let _,t2,_,_,_,_,_,_,_,_,_ = row in t2)) TextRuntime.ConvertStringBack(Some (let _,_,t3,_,_,_,_,_,_,_,_ = row in t3)) TextRuntime.ConvertStringBack(Some (let _,_,_,t4,_,_,_,_,_,_,_ = row in t4)) @@ -145,7 +145,7 @@ class CsvProvider : FDR.CsvFile static member GetSample: () -> CsvProvider let stringArrayToRow = new Func<_,_,_>(fun (parent:obj) (row:string[]) -> let value = TextConversions.AsString(row.[0]) - TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("fr-FR", value), value), + TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("nb-NO", value), value), let value = TextConversions.AsString(row.[1]) TextRuntime.GetNonOptionalValue("USD", TextRuntime.ConvertString(value), value), let value = TextConversions.AsString(row.[2]) @@ -167,7 +167,7 @@ class CsvProvider : FDR.CsvFile let value = TextConversions.AsString(row.[10]) TextRuntime.GetNonOptionalValue("AUD", TextRuntime.ConvertString(value), value)) let rowToStringArray = new Func<_,_>(fun (row:_ * _ * _ * _ * _ * _ * _ * _) -> - [| TextRuntime.ConvertDateTimeBack("fr-FR", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) + [| TextRuntime.ConvertDateTimeBack("nb-NO", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) TextRuntime.ConvertStringBack(Some (let _,t2,_,_,_,_,_,_,_,_,_ = row in t2)) TextRuntime.ConvertStringBack(Some (let _,_,t3,_,_,_,_,_,_,_,_ = row in t3)) TextRuntime.ConvertStringBack(Some (let _,_,_,t4,_,_,_,_,_,_,_ = row in t4)) @@ -183,7 +183,7 @@ class CsvProvider : FDR.CsvFile static member Load: stream:System.IO.Stream -> CsvProvider let stringArrayToRow = new Func<_,_,_>(fun (parent:obj) (row:string[]) -> let value = TextConversions.AsString(row.[0]) - TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("fr-FR", value), value), + TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("nb-NO", value), value), let value = TextConversions.AsString(row.[1]) TextRuntime.GetNonOptionalValue("USD", TextRuntime.ConvertString(value), value), let value = TextConversions.AsString(row.[2]) @@ -205,7 +205,7 @@ class CsvProvider : FDR.CsvFile let value = TextConversions.AsString(row.[10]) TextRuntime.GetNonOptionalValue("AUD", TextRuntime.ConvertString(value), value)) let rowToStringArray = new Func<_,_>(fun (row:_ * _ * _ * _ * _ * _ * _ * _) -> - [| TextRuntime.ConvertDateTimeBack("fr-FR", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) + [| TextRuntime.ConvertDateTimeBack("nb-NO", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) TextRuntime.ConvertStringBack(Some (let _,t2,_,_,_,_,_,_,_,_,_ = row in t2)) TextRuntime.ConvertStringBack(Some (let _,_,t3,_,_,_,_,_,_,_,_ = row in t3)) TextRuntime.ConvertStringBack(Some (let _,_,_,t4,_,_,_,_,_,_,_ = row in t4)) @@ -221,7 +221,7 @@ class CsvProvider : FDR.CsvFile static member Load: reader:System.IO.TextReader -> CsvProvider let stringArrayToRow = new Func<_,_,_>(fun (parent:obj) (row:string[]) -> let value = TextConversions.AsString(row.[0]) - TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("fr-FR", value), value), + TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("nb-NO", value), value), let value = TextConversions.AsString(row.[1]) TextRuntime.GetNonOptionalValue("USD", TextRuntime.ConvertString(value), value), let value = TextConversions.AsString(row.[2]) @@ -243,7 +243,7 @@ class CsvProvider : FDR.CsvFile let value = TextConversions.AsString(row.[10]) TextRuntime.GetNonOptionalValue("AUD", TextRuntime.ConvertString(value), value)) let rowToStringArray = new Func<_,_>(fun (row:_ * _ * _ * _ * _ * _ * _ * _) -> - [| TextRuntime.ConvertDateTimeBack("fr-FR", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) + [| TextRuntime.ConvertDateTimeBack("nb-NO", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) TextRuntime.ConvertStringBack(Some (let _,t2,_,_,_,_,_,_,_,_,_ = row in t2)) TextRuntime.ConvertStringBack(Some (let _,_,t3,_,_,_,_,_,_,_,_ = row in t3)) TextRuntime.ConvertStringBack(Some (let _,_,_,t4,_,_,_,_,_,_,_ = row in t4)) @@ -259,7 +259,7 @@ class CsvProvider : FDR.CsvFile static member Load: uri:string -> CsvProvider let stringArrayToRow = new Func<_,_,_>(fun (parent:obj) (row:string[]) -> let value = TextConversions.AsString(row.[0]) - TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("fr-FR", value), value), + TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("nb-NO", value), value), let value = TextConversions.AsString(row.[1]) TextRuntime.GetNonOptionalValue("USD", TextRuntime.ConvertString(value), value), let value = TextConversions.AsString(row.[2]) @@ -281,7 +281,7 @@ class CsvProvider : FDR.CsvFile let value = TextConversions.AsString(row.[10]) TextRuntime.GetNonOptionalValue("AUD", TextRuntime.ConvertString(value), value)) let rowToStringArray = new Func<_,_>(fun (row:_ * _ * _ * _ * _ * _ * _ * _) -> - [| TextRuntime.ConvertDateTimeBack("fr-FR", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) + [| TextRuntime.ConvertDateTimeBack("nb-NO", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) TextRuntime.ConvertStringBack(Some (let _,t2,_,_,_,_,_,_,_,_,_ = row in t2)) TextRuntime.ConvertStringBack(Some (let _,_,t3,_,_,_,_,_,_,_,_ = row in t3)) TextRuntime.ConvertStringBack(Some (let _,_,_,t4,_,_,_,_,_,_,_ = row in t4)) @@ -297,7 +297,7 @@ class CsvProvider : FDR.CsvFile static member Parse: text:string -> CsvProvider let stringArrayToRow = new Func<_,_,_>(fun (parent:obj) (row:string[]) -> let value = TextConversions.AsString(row.[0]) - TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("fr-FR", value), value), + TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("nb-NO", value), value), let value = TextConversions.AsString(row.[1]) TextRuntime.GetNonOptionalValue("USD", TextRuntime.ConvertString(value), value), let value = TextConversions.AsString(row.[2]) @@ -319,7 +319,7 @@ class CsvProvider : FDR.CsvFile let value = TextConversions.AsString(row.[10]) TextRuntime.GetNonOptionalValue("AUD", TextRuntime.ConvertString(value), value)) let rowToStringArray = new Func<_,_>(fun (row:_ * _ * _ * _ * _ * _ * _ * _) -> - [| TextRuntime.ConvertDateTimeBack("fr-FR", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) + [| TextRuntime.ConvertDateTimeBack("nb-NO", Some (let t1,_,_,_,_,_,_,_,_,_,_ = row in t1)) TextRuntime.ConvertStringBack(Some (let _,t2,_,_,_,_,_,_,_,_,_ = row in t2)) TextRuntime.ConvertStringBack(Some (let _,_,t3,_,_,_,_,_,_,_,_ = row in t3)) TextRuntime.ConvertStringBack(Some (let _,_,_,t4,_,_,_,_,_,_,_ = row in t4)) @@ -335,7 +335,7 @@ class CsvProvider : FDR.CsvFile static member ParseRows: text:string -> CsvProvider+CsvProvider+Row[] let stringArrayToRow = new Func<_,_,_>(fun (parent:obj) (row:string[]) -> let value = TextConversions.AsString(row.[0]) - TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("fr-FR", value), value), + TextRuntime.GetNonOptionalValue("Dato", TextRuntime.ConvertDateTime("nb-NO", value), value), let value = TextConversions.AsString(row.[1]) TextRuntime.GetNonOptionalValue("USD", TextRuntime.ConvertString(value), value), let value = TextConversions.AsString(row.[2]) diff --git a/tests/FSharp.Data.Tests/CsvProvider.fs b/tests/FSharp.Data.Tests/CsvProvider.fs index 2f729c07c..16c5c6571 100644 --- a/tests/FSharp.Data.Tests/CsvProvider.fs +++ b/tests/FSharp.Data.Tests/CsvProvider.fs @@ -7,6 +7,7 @@ open System.IO open FSharp.Data.UnitSystems.SI.UnitNames open FSharp.Data open FSharp.Data.Runtime.CsvInference +open System.Globalization let [] simpleCsv = """ Column1,Column2,Column3 @@ -141,9 +142,17 @@ let ``Infers type of an emtpy CSV file`` () = let expected : string array = [||] actual |> should equal expected +[] +let norwayCultureName = "nb-NO" + [] let ``Does not treat invariant culture number such as 3.14 as a date in cultures using 3,14`` () = - let csv = CsvProvider<"Data/DnbHistoriskeKurser.csv", ",", 10, Culture="fr-FR">.GetSample() + let targetCulture = CultureInfo(norwayCultureName) + // Make sure assumptions about the culture hold: + targetCulture.DateTimeFormat.DateSeparator |> should equal "." + targetCulture.DateTimeFormat.TimeSeparator |> should equal ":" // See https://github.com/fsprojects/FSharp.Data/issues/767 + targetCulture.NumberFormat.NumberDecimalSeparator |> should equal "," + let csv = CsvProvider<"Data/DnbHistoriskeKurser.csv", ",", 10, Culture=norwayCultureName>.GetSample() let row = csv.Rows |> Seq.head (row.Dato, row.USD) |> should equal (DateTime(2013, 2, 7), "5.4970") From 819aedac640a39e16425cad6c5e2188a1c512744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Melvyn=20La=C3=AFly?= Date: Fri, 3 Jun 2022 20:29:04 +0200 Subject: [PATCH 2/5] Fix typos in comments and docs + add some doc --- CONTRIBUTING.md | 10 +++++++--- src/CommonProviderImplementation/Helpers.fs | 2 +- src/CommonRuntime/StructuralInference.fs | 2 +- src/CommonRuntime/StructuralTypes.fs | 1 + src/CommonRuntime/TextConversions.fs | 10 +++++----- src/Csv/CsvInference.fs | 4 ++-- src/Csv/CsvProvider.fs | 4 ++-- src/Html/HtmlProvider.fs | 2 +- src/Json/JsonInference.fs | 4 ++-- src/Json/JsonProvider.fs | 2 +- src/Xml/XmlInference.fs | 2 +- 11 files changed, 24 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 29e551280..62a9917a7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,7 @@ Type providers consist of two components: (that are mapped to runtime components by the compiler). We need a _runtime component_ for .NET Standard 2.0 (netstandard2.0). We also need a _design time_ -component for each, to be able to to host the type provider in .NET Core-based tooling. +component for each, to be able to host the type provider in .NET Core-based tooling. The _runtime_ components are in the following project: @@ -68,7 +68,7 @@ of files, typically like this: the common API in `StructureInference.fs`. * `JsonGenerator.fs` - implements code that generates provided types, adds properties - and methods etc. This uses the information infered by inference and it generates + and methods etc. This uses the information inferred by inference and it generates calls to the runtime components. * `JsonProvider.fs` - entry point that defines static properties of the type provider, @@ -79,7 +79,11 @@ between _runtime_ and _design-time_ components, so you'll find at least two file ### Debugging -To debug the type generation, the best way is to change `FSharp.Data.DesignTime` project to a Console application, rename `Test.fsx` to `Test.fs` and hit the Run command in the IDE, setting the breakpoints where you need them. This will invoke all the type providers manually without locking the files in Visual Studio / Xamarin Studio. You'll also see in the console output the complete dump of the generated types and expressions. This is also the process used for the signature tests. +To debug the type generation, the best way is to change `FSharp.Data.DesignTime` project to a Console application, +rename `Test.fsx` to `Test.fs` and hit the Run command in the IDE, setting the breakpoints where you need them. +This will invoke all the type providers manually without locking the files in Visual Studio / Xamarin Studio. +You'll also see in the console output the complete dump of the generated types and expressions. +This is also the process used for the signature tests. ## Documentation diff --git a/src/CommonProviderImplementation/Helpers.fs b/src/CommonProviderImplementation/Helpers.fs index 74cdc49d6..72a61745a 100644 --- a/src/CommonProviderImplementation/Helpers.fs +++ b/src/CommonProviderImplementation/Helpers.fs @@ -338,7 +338,7 @@ module internal ProviderHelpers = let private providedTypesCache = createInMemoryCache (TimeSpan.FromSeconds 30.0) let private activeDisposeActions = HashSet<_>() - // Cache generated types for a short time, since VS invokes the TP multiple tiems + // Cache generated types for a short time, since VS invokes the TP multiple times // Also cache temporarily during partial invalidation since the invalidation of one TP always causes invalidation of all TPs let internal getOrCreateProvidedType (cfg: TypeProviderConfig) diff --git a/src/CommonRuntime/StructuralInference.fs b/src/CommonRuntime/StructuralInference.fs index 0891afe89..a75300c3e 100644 --- a/src/CommonRuntime/StructuralInference.fs +++ b/src/CommonRuntime/StructuralInference.fs @@ -190,7 +190,7 @@ let rec subtypeInfered allowEmptyValues ot1 ot2 = | InferedType.Heterogeneous h, other | other, InferedType.Heterogeneous h -> // Add the other type as another option. We should never add - // heterogenous type as an option of other heterogeneous type. + // heterogeneous type as an option of other heterogeneous type. assert (typeTag other <> InferedTypeTag.Heterogeneous) InferedType.Heterogeneous(unionHeterogeneousTypes allowEmptyValues h (Map.ofSeq [ typeTag other, other ])) diff --git a/src/CommonRuntime/StructuralTypes.fs b/src/CommonRuntime/StructuralTypes.fs index 733560bf1..7263c2976 100644 --- a/src/CommonRuntime/StructuralTypes.fs +++ b/src/CommonRuntime/StructuralTypes.fs @@ -34,6 +34,7 @@ type InferedTypeTag = | Number | Boolean | String + /// Allow for support of embedded json in e.g. xml documents | Json | DateTime | TimeSpan diff --git a/src/CommonRuntime/TextConversions.fs b/src/CommonRuntime/TextConversions.fs index f6dede45c..90e896f3a 100644 --- a/src/CommonRuntime/TextConversions.fs +++ b/src/CommonRuntime/TextConversions.fs @@ -1,4 +1,4 @@ -// -------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------- // Helper operations for converting converting string values to other types // -------------------------------------------------------------------------------------- @@ -86,8 +86,8 @@ type TextConversions private () = static member val private DefaultRemovableAdornerCharacters = Set.union TextConversions.DefaultNonCurrencyAdorners TextConversions.DefaultCurrencyAdorners - //This removes any adorners that might otherwise casue the inference to infer string. A notable a change is - //Currency Symbols are now treated as an Adorner like a '%' sign thus are now independant + //This removes any adorners that might otherwise cause the inference to infer string. A notable a change is + //Currency Symbols are now treated as an Adorner like a '%' sign thus are now independent //of the culture. Which is probably better since we have lots of scenarios where we want to //consume values prefixed with € or $ but in a different culture. static member private RemoveAdorners(value: string) = @@ -157,7 +157,7 @@ type TextConversions private () = | x -> x static member AsDateTimeOffset cultureInfo (text: string) = - // get TimeSpan presentation from 4-digt integers like 0000 or -0600 + // get TimeSpan presentation from 4-digit integers like 0000 or -0600 let getTimeSpanFromHourMin (hourMin: int) = let hr = (hourMin / 100) |> float |> TimeSpan.FromHours let min = (hourMin % 100) |> float |> TimeSpan.FromMinutes @@ -202,7 +202,7 @@ module internal UnicodeHelper = // used http://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF as a guide below let getUnicodeSurrogatePair num = // only code points U+010000 to U+10FFFF supported - // for coversion to UTF16 surrogate pair + // for conversion to UTF16 surrogate pair let codePoint = num - 0x010000u let HIGH_TEN_BIT_MASK = 0xFFC00u // 1111|1111|1100|0000|0000 let LOW_TEN_BIT_MASK = 0x003FFu // 0000|0000|0011|1111|1111 diff --git a/src/Csv/CsvInference.fs b/src/Csv/CsvInference.fs index 055116a42..afc089381 100644 --- a/src/Csv/CsvInference.fs +++ b/src/Csv/CsvInference.fs @@ -111,7 +111,7 @@ let private parseTypeAndUnit unitsOfMeasureProvider str = /// Parse schema specification for column. This can either be a name /// with type or just type: name (typeInfo)|typeInfo. -/// If forSchemaOverride is set to true, only Full or Name is returne +/// If forSchemaOverride is set to true, only Full or Name is returned /// (if we succeed we override the inferred schema, otherwise, we just /// override the header name) let private parseSchemaItem unitsOfMeasureProvider str forSchemaOverride = @@ -424,7 +424,7 @@ type CsvFile with /// Infers the types of the columns of a CSV file /// /// - Number of rows to use for inference. If this is zero, all rows are used - /// - The set of strings recogized as missing values + /// - The set of strings recognized as missing values /// - The culture used for parsing numbers and dates /// - Optional column types, in a comma separated list. Valid types are "int", "int64", "bool", "float", "decimal", "date", "timespan", "guid", "string", "int?", "int64?", "bool?", "float?", "decimal?", "date?", "guid?", "int option", "int64 option", "bool option", "float option", "decimal option", "date option", "guid option" and "string option". You can also specify a unit and the name of the column like this: Name (type<unit>). You can also override only the name. If you don't want to specify all the columns, you can specify by name like this: 'ColumnName=type' /// - Assumes all columns can have missing values diff --git a/src/Csv/CsvProvider.fs b/src/Csv/CsvProvider.fs index 9d3324c3e..6482608df 100644 --- a/src/Csv/CsvProvider.fs +++ b/src/Csv/CsvProvider.fs @@ -73,7 +73,7 @@ type public CsvProvider(cfg: TypeProviderConfig) as this = let value = if sample = "" then - // synthetize sample from the schema + // synthesize sample from the schema use reader = new StringReader(value) let schemaStr = @@ -235,7 +235,7 @@ type public CsvProvider(cfg: TypeProviderConfig) as this = When set to true, the type provider will assume all columns can have missing values, even if in the provided sample all values are present. Defaults to false. When set to true, inference will prefer to use the option type instead of nullable types, double.NaN or "" for missing values. Defaults to false. The quotation mark (for surrounding values containing the delimiter). Defaults to ". - The set of strings recogized as missing values specified as a comma-separated string (e.g., "NA,N/A"). Defaults to """ + The set of strings recognized as missing values specified as a comma-separated string (e.g., "NA,N/A"). Defaults to """ + String.Join(",", TextConversions.DefaultMissingValues) + """. Whether the rows should be caches so they can be iterated multiple times. Defaults to true. Disable for large datasets. diff --git a/src/Html/HtmlProvider.fs b/src/Html/HtmlProvider.fs index a2ea01dfe..6363b83ef 100644 --- a/src/Html/HtmlProvider.fs +++ b/src/Html/HtmlProvider.fs @@ -86,7 +86,7 @@ type public HtmlProvider(cfg: TypeProviderConfig) as this = Location of an HTML sample file or a string containing a sample HTML document. When set to true, inference will prefer to use the option type instead of nullable types, double.NaN or "" for missing values. Defaults to false. Includes tables that are potentially layout tables (with cellpadding=0 and cellspacing=0 attributes) - The set of strings recogized as missing values. Defaults to """ + The set of strings recognized as missing values. Defaults to """ + String.Join(",", TextConversions.DefaultMissingValues) + """. The culture used for parsing numbers and dates. Defaults to the invariant culture. diff --git a/src/Json/JsonInference.fs b/src/Json/JsonInference.fs index 308a798d6..d2d3303c6 100644 --- a/src/Json/JsonInference.fs +++ b/src/Json/JsonInference.fs @@ -9,7 +9,7 @@ open FSharp.Data open FSharp.Data.Runtime open FSharp.Data.Runtime.StructuralTypes -/// Infer type of a JSON value - this is simple function because most of the +/// Infer type of a JSON value - this is a simple function because most of the /// functionality is handled in `StructureInference` (most notably, by /// `inferCollectionType` and various functions to find common subtype), so /// here we just need to infer types of primitive JSON values. @@ -20,7 +20,7 @@ let rec inferType inferTypesFromValues cultureInfo parentName json = let inline isIntegerFloat (v: float) : bool = Math.Round v = v match json with - // Null and primitives without subtyping hiearchies + // Null and primitives without subtyping hierarchies | JsonValue.Null -> InferedType.Null | JsonValue.Boolean _ -> InferedType.Primitive(typeof, None, false) | JsonValue.String s when inferTypesFromValues -> StructuralInference.getInferedTypeFromString cultureInfo s None diff --git a/src/Json/JsonProvider.fs b/src/Json/JsonProvider.fs index 8dc171e7d..acfc8ea78 100644 --- a/src/Json/JsonProvider.fs +++ b/src/Json/JsonProvider.fs @@ -117,7 +117,7 @@ type public JsonProvider(cfg: TypeProviderConfig) as this = (e.g. 'MyCompany.MyAssembly, resource_name.json'). This is useful when exposing types generated by the type provider. If true, turns on additional type inference from values. (e.g. type inference infers string values such as "123" as ints and values constrained to 0 and 1 as booleans.) - If true, json record is considered as a dictionary, if the names of all the its fields are infered (by type inference rules) into the same non-string primitive type.""" + If true, json records are interpreted as dictionaries when the names of all the fields are infered (by type inference rules) into the same non-string primitive type.""" do jsonProvTy.AddXmlDoc helpText do jsonProvTy.DefineStaticParameters(parameters, buildTypes) diff --git a/src/Xml/XmlInference.fs b/src/Xml/XmlInference.fs index 1d278fa1f..16cbd4964 100644 --- a/src/Xml/XmlInference.fs +++ b/src/Xml/XmlInference.fs @@ -60,7 +60,7 @@ let getInferedTypeFromValue inferTypesFromValues cultureInfo (element: XElement) InferedType.Primitive(typeof, None, false) /// Infers type for the element, unifying nodes of the same name -/// accross the entire document (we first get information based +/// across the entire document (we first get information based /// on just attributes and then use a fixed point) let inferGlobalType inferTypesFromValues cultureInfo allowEmptyValues (elements: XElement[]) = From 0fae3ce08b3abc8a42ef529f55fe85452f8bdca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Melvyn=20La=C3=AFly?= Date: Thu, 9 Jun 2022 08:17:35 +0200 Subject: [PATCH 3/5] Skip comments of the form // or /**/ when parsing json --- src/Json/JsonValue.fs | 62 ++++++++++++++----- .../FSharp.Data.Tests/JsonParserProperties.fs | 27 ++++++++ 2 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/Json/JsonValue.fs b/src/Json/JsonValue.fs index 557381c48..e8b1c12c0 100644 --- a/src/Json/JsonValue.fs +++ b/src/Json/JsonValue.fs @@ -164,9 +164,6 @@ type private JsonParser(jsonText: string) = let buf = StringBuilder() // pre-allocate buffers for strings // Helper functions - let skipWhitespace () = - while i < s.Length && Char.IsWhiteSpace s.[i] do - i <- i + 1 let isNumChar c = Char.IsDigit c @@ -191,9 +188,42 @@ type private JsonParser(jsonText: string) = let ensure cond = if not cond then throw () + + let rec skipCommentsAndWhitespace () = + let skipComment () = + // Supported comment syntax: + // - // ...{newLine} + // - /* ... */ + if i < s.Length && s.[i] = '/' then + i <- i + 1 + + if i < s.Length && s.[i] = '/' then + i <- i + 1 + while i < s.Length && (s.[i] <> '\r' && s.[i] <> '\n') do + i <- i + 1 + else if i < s.Length && s.[i] = '*' then + i <- i + 1 + while i + 1 < s.Length && s.[i] <> '*' && s.[i + 1] <> '/' do + i <- i + 1 + ensure (i + 1 < s.Length && s.[i] = '*' && s.[i + 1] = '/') + i <- i + 2 + true + + else + false + + let skipWhitespace () = + let initialI = i + while i < s.Length && Char.IsWhiteSpace s.[i] do + i <- i + 1 + initialI <> i // return true if some whitespace was skipped + + if skipWhitespace () || skipComment () then + skipCommentsAndWhitespace () + // Recursive descent parser for JSON that uses global mutable index let rec parseValue cont = - skipWhitespace () + skipCommentsAndWhitespace () ensure (i < s.Length) match s.[i] with @@ -291,16 +321,16 @@ type private JsonParser(jsonText: string) = and parsePair cont = let key = parseString () - skipWhitespace () + skipCommentsAndWhitespace () ensure (i < s.Length && s.[i] = ':') i <- i + 1 - skipWhitespace () + skipCommentsAndWhitespace () parseValue (fun v -> cont (key, v)) and parseObject cont = ensure (i < s.Length && s.[i] = '{') i <- i + 1 - skipWhitespace () + skipCommentsAndWhitespace () let pairs = ResizeArray<_>() let parseObjectEnd () = @@ -312,16 +342,16 @@ type private JsonParser(jsonText: string) = if i < s.Length && s.[i] = '"' then parsePair (fun p -> pairs.Add(p) - skipWhitespace () + skipCommentsAndWhitespace () let rec parsePairItem () = if i < s.Length && s.[i] = ',' then i <- i + 1 - skipWhitespace () + skipCommentsAndWhitespace () parsePair (fun p -> pairs.Add(p) - skipWhitespace () + skipCommentsAndWhitespace () parsePairItem ()) else parseObjectEnd () @@ -333,7 +363,7 @@ type private JsonParser(jsonText: string) = and parseArray cont = ensure (i < s.Length && s.[i] = '[') i <- i + 1 - skipWhitespace () + skipCommentsAndWhitespace () let vals = ResizeArray<_>() let parseArrayEnd () = @@ -345,16 +375,16 @@ type private JsonParser(jsonText: string) = if i < s.Length && s.[i] <> ']' then parseValue (fun v -> vals.Add(v) - skipWhitespace () + skipCommentsAndWhitespace () let rec parseArrayItem () = if i < s.Length && s.[i] = ',' then i <- i + 1 - skipWhitespace () + skipCommentsAndWhitespace () parseValue (fun v -> vals.Add(v) - skipWhitespace () + skipCommentsAndWhitespace () parseArrayItem ()) else parseArrayEnd () @@ -375,7 +405,7 @@ type private JsonParser(jsonText: string) = // Start by parsing the top-level value member x.Parse() = let value = parseValue id - skipWhitespace () + skipCommentsAndWhitespace () if i <> s.Length then throw () value @@ -383,7 +413,7 @@ type private JsonParser(jsonText: string) = seq { while i <> s.Length do yield parseValue id - skipWhitespace () + skipCommentsAndWhitespace () } type JsonValue with diff --git a/tests/FSharp.Data.Tests/JsonParserProperties.fs b/tests/FSharp.Data.Tests/JsonParserProperties.fs index 41e703f6a..c50b003b6 100644 --- a/tests/FSharp.Data.Tests/JsonParserProperties.fs +++ b/tests/FSharp.Data.Tests/JsonParserProperties.fs @@ -160,3 +160,30 @@ let ``Stringifing parsed string returns the same string (UNICODE)`` () = let jsonConverted = jsonValue.ToString(JsonSaveOptions.DisableFormatting) Assert.AreNotEqual(input, jsonConverted) // this now has the escaped value Assert.AreEqual(unescape input, jsonConverted) + +[] +let ``Single line is skipped`` () = + let input = + """// + { + "test": true // x + } + //""" + let inputWithoutComment = """{"test": true}""" + let jsonValue = JsonValue.Parse input + let jsonValueWithoutComment = JsonValue.Parse inputWithoutComment + Assert.AreEqual(jsonValueWithoutComment, jsonValue) + +let ``Multiline comment is skipped`` () = + let input = + """/**/ + {"test": true} + /**//**/ /**/ // + /* { + "test": true + } + */""" + let inputWithoutComment = """{"test": true}""" + let jsonValue = JsonValue.Parse input + let jsonValueWithoutComment = JsonValue.Parse inputWithoutComment + Assert.AreEqual(jsonValueWithoutComment, jsonValue) From 60c96e806deb2341b73d3f3a405c6149f38ea120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Melvyn=20La=C3=AFly?= Date: Thu, 9 Jun 2022 08:30:25 +0200 Subject: [PATCH 4/5] Fix .gitattributes to be consistent with .editorconfig (EOL=lf) --- .gitattributes | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index fc41139c4..1b382a5b0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ # Auto detect text files and perform LF normalization -* text=auto +* text=auto eol=lf # Custom for Visual Studio *.cs text diff=csharp @@ -22,6 +22,8 @@ *.RTF diff=astextplain *.sh text eol=lf +*.cmd text eol=crlf +*.bat text eol=crlf *.png binary *.exe binary From 37ecab2c9cc6534276c9144892f9cabccac22e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Melvyn=20La=C3=AFly?= Date: Thu, 9 Jun 2022 08:37:05 +0200 Subject: [PATCH 5/5] dotnet fake build -t Format --- src/Json/JsonValue.fs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Json/JsonValue.fs b/src/Json/JsonValue.fs index e8b1c12c0..708ab1fb1 100644 --- a/src/Json/JsonValue.fs +++ b/src/Json/JsonValue.fs @@ -199,14 +199,20 @@ type private JsonParser(jsonText: string) = if i < s.Length && s.[i] = '/' then i <- i + 1 + while i < s.Length && (s.[i] <> '\r' && s.[i] <> '\n') do i <- i + 1 else if i < s.Length && s.[i] = '*' then i <- i + 1 - while i + 1 < s.Length && s.[i] <> '*' && s.[i + 1] <> '/' do + + while i + 1 < s.Length + && s.[i] <> '*' + && s.[i + 1] <> '/' do i <- i + 1 + ensure (i + 1 < s.Length && s.[i] = '*' && s.[i + 1] = '/') i <- i + 2 + true else @@ -214,8 +220,10 @@ type private JsonParser(jsonText: string) = let skipWhitespace () = let initialI = i + while i < s.Length && Char.IsWhiteSpace s.[i] do i <- i + 1 + initialI <> i // return true if some whitespace was skipped if skipWhitespace () || skipComment () then