Add a json parser
This commit is contained in:
parent
2c0c5b628f
commit
9d7779e8ca
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,2 +1,5 @@
|
||||
.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