Skip to content

Commit

Permalink
feat(num): implement number string decode
Browse files Browse the repository at this point in the history
  • Loading branch information
ernado committed Nov 5, 2021
1 parent f2c132f commit 80786b3
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 8 deletions.
29 changes: 25 additions & 4 deletions dec_num.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,40 @@ func (d *Decoder) NumAppend(v Num) (Num, error) {

// num decodes number.
func (d *Decoder) num(v Num, forceAppend bool) (Num, error) {
var str bool
switch d.Next() {
case String:
// Consume start of the string.
d.head++
str = true
case Number: // float or integer
default:
return v, errors.Errorf("unexpected %s", d.Next())
}
if d.reader == nil && !forceAppend {
// Can use underlying buffer directly.
v = d.number()
start := d.head
d.head++
d.number()
if str {
if err := d.consume('"'); err != nil {
return nil, errors.Wrap(err, "end of string")
}
}
v = d.buf[start:d.head]
} else {
buf, err := d.numberAppend(v[:0])
if str {
d.head++ // '"'
v = append(v, '"')
}
buf, err := d.numberAppend(v)
if err != nil {
return v, errors.Wrap(err, "decode")
}
if str {
if err := d.consume('"'); err != nil {
return nil, errors.Wrap(err, "end of string")
}
buf = append(buf, '"')
}
v = buf
}

Expand All @@ -47,6 +65,9 @@ func (d *Decoder) num(v Num, forceAppend bool) (Num, error) {
}

// TODO(ernado): Additional validity checks
// Current invariants:
// 1) Zero or one dot
// 2) Only: +, -, ., e, E, 0-9

return v, nil
}
43 changes: 43 additions & 0 deletions dec_num_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package jx

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestDecoder_Num(t *testing.T) {
t.Run("Positive", func(t *testing.T) {
for _, s := range []string{
`100`,
`100.0`,
`-100.0`,
`-100`,
`"-100"`,
`"-100.0"`,
} {
v, err := DecodeStr(s).Num()
require.NoError(t, err)
require.Equal(t, s, v.String())

v, err = DecodeStr(s).NumAppend(nil)
require.NoError(t, err)
require.Equal(t, s, v.String())
}
})
t.Run("Negative", func(t *testing.T) {
for _, s := range []string{
`1.00.0`,
`"-100`,
`"-100.0.0"`,
"false",
`"false"`,
} {
_, err := DecodeStr(s).Num()
require.Error(t, err, s)

_, err = DecodeStr(s).NumAppend(nil)
require.Error(t, err, s)
}
})
}
2 changes: 1 addition & 1 deletion num.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func (n Num) Zero() bool {
}
for _, c := range n {
switch c {
case '.', '0':
case '.', '0', '-':
continue
default:
return false
Expand Down
33 changes: 30 additions & 3 deletions num_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ import (
)

func TestEncoder_Num(t *testing.T) {
var e Encoder
e.Num(Num{'1', '2', '3'})
require.Equal(t, e.String(), "123")
t.Run("Valid", func(t *testing.T) {
var e Encoder
e.Num(Num{'1', '2', '3'})
require.Equal(t, e.String(), "123")
})
t.Run("Invalid", func(t *testing.T) {
var e Encoder
e.Num(Num{})
require.Equal(t, e.String(), "null")
})
}

func TestNum(t *testing.T) {
Expand Down Expand Up @@ -48,6 +55,8 @@ func TestNum(t *testing.T) {
assert.True(t, v.Equal(v))
assert.True(t, v.IsInt())
assert.False(t, v.Equal(Num{}))

assert.Equal(t, 0, Num{'"'}.Sign())
})
t.Run("ZeroValue", func(t *testing.T) {
// Zero value is invalid because there is no Num.Value.
Expand All @@ -59,6 +68,24 @@ func TestNum(t *testing.T) {
require.False(t, v.Str())
require.Equal(t, "<invalid>", v.String())
})
t.Run("IntZero", func(t *testing.T) {
v := Num{'0'}
require.True(t, v.Zero())
require.False(t, v.Positive())
require.False(t, v.Negative())
require.True(t, v.IsInt())
require.False(t, v.Str())
require.Equal(t, "0", v.String())
})
t.Run("FloatZero", func(t *testing.T) {
v := Num{'-', '0', '.', '0'}
require.True(t, v.Zero())
require.False(t, v.Positive())
require.True(t, v.Negative())
require.False(t, v.IsInt())
require.False(t, v.Str())
require.Equal(t, "-0.0", v.String())
})
t.Run("Integer", func(t *testing.T) {
t.Run("Int", func(t *testing.T) {
v := Num{'1', '2', '3'}
Expand Down

0 comments on commit 80786b3

Please sign in to comment.