fjoppe / Legivel

F# Yaml 1.2 parser
https://fjoppe.github.io/Legivel
The Unlicense
60 stars 5 forks source link

Unhandled exception is thrown when parsing float value #1

Closed ziemiowid closed 6 years ago

ziemiowid commented 6 years ago

Description

Unhandled exception is thrown when deserializing float value in culture context not using a dot as decimal separator.

Repro steps

Under Linux:

  1. Set language environment to Polish, and run fsi.exe LANG=pl_PL.utf8 mono fsi.exe

  2. Run script:

    #r "Legivel.Mapper.dll"
    open Legivel.Serialization
    
    Deserialize<float> "3.14"

Expected behavior

String "3.14" should be parsed as float value:

> Deserialize<float>("3.14");;
val it : DeserializeResult<float> list = [Succes {Data = 3.14;
                                                  Warn = [];}]

Actual behavior

Exception is thown:

> Deserialize<float>("3.14");;
System.FormatException: Input string was not in a correct format.
  at System.Number.ParseDouble (System.String value, System.Globalization.NumberStyles options, System.Globalization.NumberFormatInfo numfmt) [0x0009f] in <8f2c484307284b51944a1a13a14c0266>:0 
  at System.Double.Parse (System.String s, System.Globalization.NumberStyles style, System.Globalization.NumberFormatInfo info) [0x00000] in <8f2c484307284b51944a1a13a14c0266>:0 
  at System.Double.Parse (System.String s) [0x0000b] in <8f2c484307284b51944a1a13a14c0266>:0 
  at Legivel.Customization.Mapping+YamlScalarToNativeMappings@28-2.Invoke (System.String s) [0x00011] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Legivel.Customization.Mapping+Legivel-Customization-Mapping-IYamlToNativeMapping-map@86.Invoke (Legivel.RepresentationGraph+NodeData`1[T] _arg2) [0x00017] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Legivel.Common+SerieBuilder.Bind[a,c,b] (Legivel.Common+FallibleOption`2[a,b] mx, Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] f) [0x00021] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Legivel.Customization.Mapping+PrimitiveMappingInfo.Legivel-Customization-Mapping-IYamlToNativeMapping-map (Legivel.RepresentationGraph+Node n) [0x00034] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Legivel.Customization.Mapping.MapYamlDocumentToNative[tp] (Legivel.Customization.Mapping+IYamlToNativeMapping mapper, Legivel.RepresentationGraph+ParsedDocumentResult pdr) [0x00008] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Legivel.Serialization+Deserialize@38-1[tp].Invoke (Legivel.Customization.Mapping+IYamlToNativeMapping mapper, Legivel.RepresentationGraph+ParsedDocumentResult pdr) [0x00001] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Microsoft.FSharp.Core.OptimizedClosures+Invoke@3253[T2,TResult,T1].Invoke (T2 u) [0x00001] in <591b093cdff9fae1a74503833c091b59>:0 
  at Legivel.Customization.Mapping+ParseYamlToNative@529[tp].Invoke (Legivel.RepresentationGraph+Representation ymlpl) [0x00092] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Microsoft.FSharp.Primitives.Basics.List.map[T,TResult] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] mapping, Microsoft.FSharp.Collections.FSharpList`1[T] x) [0x0001f] in <591b093cdff9fae1a74503833c091b59>:0 
  at Microsoft.FSharp.Collections.ListModule.Map[T,TResult] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] mapping, Microsoft.FSharp.Collections.FSharpList`1[T] list) [0x00001] in <591b093cdff9fae1a74503833c091b59>:0 
  at Legivel.Customization.Mapping.ParseYamlToNative[tp] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] mapToNative, Legivel.TagResolution+GlobalTagSchema schema, System.String yml) [0x00015] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Legivel.Serialization+Deserialize@38-2[tp].Invoke (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] mapToNative, Legivel.TagResolution+GlobalTagSchema schema, System.String yml) [0x00001] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Microsoft.FSharp.Core.FSharpFunc`2[T,TResult].InvokeFast[V,W] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] func, T arg1, TResult arg2, V arg3) [0x0000d] in <591b093cdff9fae1a74503833c091b59>:0 
  at Legivel.Customization.Mapping.CustomDeserializeYaml[tp] (Microsoft.FSharp.Collections.FSharpList`1[T] tryFindMappers, Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] mapYmlDocToNative, Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] parseYmlToNative, Legivel.TagResolution+GlobalTagSchema schema, System.String nullTagUri, System.String yml) [0x000ad] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at Legivel.Serialization.Deserialize[tp] (System.String yaml) [0x00024] in <59f4d090a4fbdd78a745038390d0f459>:0 
  at <StartupCode$FSI_0003>.$FSI_0003.main@ () [0x00001] in <27ee1afc029747b4abf87ade13e34d7f>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00038] in <8f2c484307284b51944a1a13a14c0266>:0 
Stopped due to error

Known workarounds

This exception can be avoided by setting proper language environment before running program, for example in Linux:

LANG=en_US.utf8 <ProgramToBeRun>

Related information

ziemiowid commented 6 years ago

I suppose that code which triggers this error is in Legivel.Mapper/Customization.fs line 28, where one can find call to Double.Parse(String).

    ScalarToNativeMapping.Create (YamlExtended.FloatGlobalTag, typeof<float>, fun (s:string) -> YamlExtended.FloatGlobalTag.ToCanonical s |> Option.get |> Double.Parse |> box)

According to .NET documentation string passed to Double.Parse(String)

is interpreted using the formatting information in a NumberFormatInfo object that is initialized for the current thread culture.

so in, for example pl_PL environment it expects coma as decimal separator and hence exception. To avoid exception more complicated form of Double.Parse(String, NumberStyles) should be used.

Unfortunately I couldn't catch this error in my debugging environment (Monodevelop), so it's only speculations.

fjoppe commented 6 years ago

Hi,

Thanks for noticing. I've fixed this issue in 0.0.7, which I published to Nuget just now. Float parsing was indeed broken - in my language we use comma as decimal seperator.

Anyway, there is now a unit test for this. Please let me know if it works on your machine.

Regards, Frank

fjoppe commented 6 years ago

I think there's no reason to keep this issue opened now.