diff --git a/lexer.go b/lexer.go index 77879dc..d11de42 100644 --- a/lexer.go +++ b/lexer.go @@ -204,6 +204,14 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn { return l.lexFalse } + if l.follow("inf") { + return l.lexInf + } + + if l.follow("nan") { + return l.lexNan + } + if isSpace(next) { l.skip() continue @@ -265,6 +273,18 @@ func (l *tomlLexer) lexFalse() tomlLexStateFn { return l.lexRvalue } +func (l *tomlLexer) lexInf() tomlLexStateFn { + l.fastForward(3) + l.emit(tokenInf) + return l.lexRvalue +} + +func (l *tomlLexer) lexNan() tomlLexStateFn { + l.fastForward(3) + l.emit(tokenNan) + return l.lexRvalue +} + func (l *tomlLexer) lexEqual() tomlLexStateFn { l.next() l.emit(tokenEqual) @@ -651,7 +671,14 @@ func (l *tomlLexer) lexNumber() tomlLexStateFn { if r == '+' || r == '-' { l.next() + if l.follow("inf") { + return l.lexInf + } + if l.follow("nan") { + return l.lexNan + } } + pointSeen := false expSeen := false digitSeen := false diff --git a/parser.go b/parser.go index b219bf7..04b1d5c 100644 --- a/parser.go +++ b/parser.go @@ -5,6 +5,7 @@ package toml import ( "errors" "fmt" + "math" "reflect" "regexp" "strconv" @@ -243,6 +244,13 @@ func (p *tomlParser) parseRvalue() interface{} { return true case tokenFalse: return false + case tokenInf: + if tok.val[0] == '-' { + return math.Inf(-1) + } + return math.Inf(1) + case tokenNan: + return math.NaN() case tokenInteger: cleanedVal := cleanupNumberToken(tok.val) var err error diff --git a/parser_test.go b/parser_test.go index 3818075..90eb0dc 100644 --- a/parser_test.go +++ b/parser_test.go @@ -2,6 +2,7 @@ package toml import ( "fmt" + "math" "reflect" "testing" "time" @@ -93,6 +94,25 @@ func TestSimpleNumbers(t *testing.T) { }) } +func TestSpecialFloats(t *testing.T) { + tree, err := Load(` +normalinf = inf +plusinf = +inf +minusinf = -inf +normalnan = nan +plusnan = +nan +minusnan = -nan +`) + assertTree(t, tree, err, map[string]interface{}{ + "normalinf": math.Inf(1), + "plusinf": math.Inf(1), + "minusinf": math.Inf(-1), + "normalnan": math.NaN(), + "plusnan": math.NaN(), + "minusnan": math.NaN(), + }) +} + func TestHexIntegers(t *testing.T) { tree, err := Load(`a = 0xDEADBEEF`) assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) diff --git a/token.go b/token.go index 5581fe0..1a90813 100644 --- a/token.go +++ b/token.go @@ -23,6 +23,8 @@ const ( tokenTrue tokenFalse tokenFloat + tokenInf + tokenNan tokenEqual tokenLeftBracket tokenRightBracket @@ -55,6 +57,8 @@ var tokenTypeNames = []string{ "True", "False", "Float", + "Inf", + "NaN", "=", "[", "]", diff --git a/tomltree_write.go b/tomltree_write.go index f5ef124..d322a97 100644 --- a/tomltree_write.go +++ b/tomltree_write.go @@ -54,9 +54,9 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen // Ensure a round float does contain a decimal point. Otherwise feeding // the output back to the parser would convert to an integer. if math.Trunc(value) == value { - return strconv.FormatFloat(value, 'f', 1, 32), nil + return strings.ToLower(strconv.FormatFloat(value, 'f', 1, 32)), nil } - return strconv.FormatFloat(value, 'f', -1, 32), nil + return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil case string: return "\"" + encodeTomlString(value) + "\"", nil case []byte: diff --git a/tomltree_write_test.go b/tomltree_write_test.go index 5ea59bc..206203b 100644 --- a/tomltree_write_test.go +++ b/tomltree_write_test.go @@ -309,6 +309,24 @@ func TestTreeWriteToFloat(t *testing.T) { } } +func TestTreeWriteToSpecialFloat(t *testing.T) { + expected := `a = +inf +b = -inf +c = nan` + + tree, err := Load(expected) + if err != nil { + t.Fatal(err) + } + str, err := tree.ToTomlString() + if err != nil { + t.Fatal(err) + } + if strings.TrimSpace(str) != strings.TrimSpace(expected) { + t.Fatalf("Expected:\n%s\nGot:\n%s", expected, str) + } +} + func BenchmarkTreeToTomlString(b *testing.B) { toml, err := Load(sampleHard) if err != nil {