Skip to content

Commit

Permalink
Add support for special float values (inf and nan) (pelletier#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
JelteF authored and pelletier committed Jan 18, 2018
1 parent a1e8a8d commit 778c285
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 2 deletions.
27 changes: 27 additions & 0 deletions lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package toml
import (
"errors"
"fmt"
"math"
"reflect"
"regexp"
"strconv"
Expand Down Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package toml

import (
"fmt"
"math"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -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)})
Expand Down
4 changes: 4 additions & 0 deletions token.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const (
tokenTrue
tokenFalse
tokenFloat
tokenInf
tokenNan
tokenEqual
tokenLeftBracket
tokenRightBracket
Expand Down Expand Up @@ -55,6 +57,8 @@ var tokenTypeNames = []string{
"True",
"False",
"Float",
"Inf",
"NaN",
"=",
"[",
"]",
Expand Down
4 changes: 2 additions & 2 deletions tomltree_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
18 changes: 18 additions & 0 deletions tomltree_write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 778c285

Please sign in to comment.