Add a json parser
This commit is contained in:
parent
2c0c5b628f
commit
9d7779e8ca
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,2 +1,5 @@
|
|||||||
.idea
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
|
*.exe
|
||||||
|
cpu.out
|
||||||
|
mem.out
|
||||||
|
68
json/json.go
Normal file
68
json/json.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
. "github.com/vektah/goparsify"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
value Parser
|
||||||
|
|
||||||
|
array = Map(And(WS, "[", Kleene(&value, And(WS, ",")), "]"), func(n Node) Node {
|
||||||
|
return n.([]Node)[1].([]Node)
|
||||||
|
})
|
||||||
|
properties = Kleene(And(WS, String('"'), WS, ":", WS, &value), ",")
|
||||||
|
object = Map(And(WS, "{", WS, properties, WS, "}"), func(n Node) Node {
|
||||||
|
ret := map[string]interface{}{}
|
||||||
|
|
||||||
|
for _, prop := range n.([]Node)[1].([]Node) {
|
||||||
|
vals := prop.([]Node)
|
||||||
|
if len(vals) == 3 {
|
||||||
|
ret[vals[0].(string)] = vals[2]
|
||||||
|
} else {
|
||||||
|
ret[vals[0].(string)] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
})
|
||||||
|
|
||||||
|
_null = Map(And(WS, "null"), func(n Node) Node {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
_true = Map(And(WS, "true"), func(n Node) Node {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
_false = Map(And(WS, "false"), func(n Node) Node {
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
Y = Map(And(&value, WS), func(n Node) Node {
|
||||||
|
nodes := n.([]Node)
|
||||||
|
if len(nodes) > 0 {
|
||||||
|
return nodes[0]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
value = Any(_null, _true, _false, String('"'), array, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unmarshal(input string) (interface{}, error) {
|
||||||
|
result, remaining, err := ParseString(Y, input)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if remaining != "" {
|
||||||
|
return result, errors.New("left unparsed: " + remaining)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
70
json/json_test.go
Normal file
70
json/json_test.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
. "github.com/vektah/goparsify"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnmarshal(t *testing.T) {
|
||||||
|
t.Run("basic types", func(t *testing.T) {
|
||||||
|
result, err := Unmarshal(`true`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, result)
|
||||||
|
|
||||||
|
result, err = Unmarshal(`false`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, false, result)
|
||||||
|
|
||||||
|
result, err = Unmarshal(`null`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, nil, result)
|
||||||
|
|
||||||
|
result, err = Unmarshal(`"true"`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "true", result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("array", func(t *testing.T) {
|
||||||
|
result, err := Unmarshal(`[true, null, false]`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []Node{true, nil, false}, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("object", func(t *testing.T) {
|
||||||
|
result, err := Unmarshal(`{"true":true, "false":false, "null": null} `)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]interface{}{"true": true, "false": false, "null": nil}, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const benchmarkString = `{"true":true, "false":false, "null": null}`
|
||||||
|
|
||||||
|
//func BenchmarkUnmarshalParsec(b *testing.B) {
|
||||||
|
// bytes := []byte(benchmarkString)
|
||||||
|
//
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// scanner := parsecJson.NewJSONScanner(bytes)
|
||||||
|
// _, remaining := parsecJson.Y(scanner)
|
||||||
|
//
|
||||||
|
// require.True(b, remaining.Endof())
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
func BenchmarkUnmarshalParsify(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := Unmarshal(benchmarkString)
|
||||||
|
require.NoError(b, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//func BenchmarkUnmarshalStdlib(b *testing.B) {
|
||||||
|
// bytes := []byte(benchmarkString)
|
||||||
|
// var result interface{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// err := stdlibJson.Unmarshal(bytes, &result)
|
||||||
|
// require.NoError(b, err)
|
||||||
|
// }
|
||||||
|
//}
|
3
json/profile/cpuprofile.bat
Normal file
3
json/profile/cpuprofile.bat
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
go build
|
||||||
|
profile.exe -cpuprofile cpu.out
|
||||||
|
go tool pprof --inuse_objects profile.exe cpu.out
|
55
json/profile/json.go
Normal file
55
json/profile/json.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
|
|
||||||
|
"github.com/vektah/goparsify/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
|
||||||
|
var memprofile = flag.String("memprofile", "", "write memory profile to this file")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
if *cpuprofile != "" {
|
||||||
|
f, err := os.Create(*cpuprofile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pprof.StartCPUProfile(f)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
err := f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if *memprofile != "" {
|
||||||
|
runtime.MemProfileRate = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
_, err := json.Unmarshal(`{"true":true, "false":false, "null": null}`)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *memprofile != "" {
|
||||||
|
f, err := os.Create(*memprofile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pprof.WriteHeapProfile(f)
|
||||||
|
f.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
3
json/profile/memprofile.bat
Normal file
3
json/profile/memprofile.bat
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
go build
|
||||||
|
profile.exe -memprofile mem.out
|
||||||
|
go tool pprof --inuse_objects profile.exe mem.out
|
Loading…
Reference in New Issue
Block a user