Add json library (encode/decode methods) to lua.

This commit is contained in:
Alex Roitman 2017-11-20 14:21:21 -08:00
parent 89662d9c89
commit 3fa209b1c0
9 changed files with 303 additions and 1 deletions

8
Gopkg.lock generated
View File

@ -222,9 +222,15 @@
revision = "f92cdcd7dcdc69e81b2d7b338479a19a8723cfa3" revision = "f92cdcd7dcdc69e81b2d7b338479a19a8723cfa3"
version = "v1.6.0" version = "v1.6.0"
[[projects]]
branch = "master"
name = "layeh.com/gopher-json"
packages = ["."]
revision = "c128cc74278be889c4381681712931976fe0d88b"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "4766bb1bebb736256ed12e864e0810ebf21e26e9fee09fa1884cda8072982155" inputs-digest = "d03f1946a6c426327998ec09c8ed935b234a8ca47e2bea55dd6f9d77424f0d60"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View File

@ -16,6 +16,7 @@ import (
"github.com/tidwall/resp" "github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/server" "github.com/tidwall/tile38/controller/server"
"github.com/yuin/gopher-lua" "github.com/yuin/gopher-lua"
luajson "layeh.com/gopher-json"
) )
const ( const (
@ -150,6 +151,9 @@ func (pl *lStatePool) New() *lua.LState {
} }
L.SetGlobal("tile38", L.SetFuncs(L.NewTable(), exports)) L.SetGlobal("tile38", L.SetFuncs(L.NewTable(), exports))
// Load json
L.SetGlobal("json", L.Get(luajson.Loader(L)))
// Prohibit creating new globals in this state // Prohibit creating new globals in this state
lockNewGlobals := func(ls *lua.LState) int { lockNewGlobals := func(ls *lua.LState) int {
ls.RaiseError("attempt to create global variable '%s'", ls.ToString(2)) ls.RaiseError("attempt to create global variable '%s'", ls.ToString(2))

22
vendor/layeh.com/gopher-json/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

7
vendor/layeh.com/gopher-json/README.md generated vendored Normal file
View File

@ -0,0 +1,7 @@
# gopher-json [![GoDoc](https://godoc.org/layeh.com/gopher-json?status.svg)](https://godoc.org/layeh.com/gopher-json)
Package json is a simple JSON encoder/decoder for [gopher-lua](https://github.com/yuin/gopher-lua).
## License
Public domain.

40
vendor/layeh.com/gopher-json/api.go generated vendored Normal file
View File

@ -0,0 +1,40 @@
package json // import "layeh.com/gopher-json"
import (
"encoding/json"
"github.com/yuin/gopher-lua"
)
var api = map[string]lua.LGFunction{
"decode": apiDecode,
"encode": apiEncode,
}
func apiDecode(L *lua.LState) int {
str := L.CheckString(1)
var value interface{}
err := json.Unmarshal([]byte(str), &value)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(fromJSON(L, value))
return 1
}
func apiEncode(L *lua.LState) int {
value := L.CheckAny(1)
visited := make(map[*lua.LTable]bool)
data, err := toJSON(value, visited)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(lua.LString(string(data)))
return 1
}

16
vendor/layeh.com/gopher-json/doc.go generated vendored Normal file
View File

@ -0,0 +1,16 @@
// Package json is a simple JSON encoder/decoder for gopher-lua.
//
// Documentation
//
// The following functions are exposed by the library:
// decode(string): Decodes a JSON string. Returns nil and an error string if
// the string could not be decoded.
// encode(value): Encodes a value into a JSON string. Returns nil and an error
// string if the value could not be encoded.
//
// Example
//
// Below is an example usage of the library:
// L := lua.NewState()
// luajson.Preload(s)
package json // import "layeh.com/gopher-json"

21
vendor/layeh.com/gopher-json/json.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
package json // import "layeh.com/gopher-json"
import (
"github.com/yuin/gopher-lua"
)
// Preload adds json to the given Lua state's package.preload table. After it
// has been preloaded, it can be loaded using require:
//
// local json = require("json")
func Preload(L *lua.LState) {
L.PreloadModule("json", Loader)
}
// Loader is the module loader function.
func Loader(L *lua.LState) int {
t := L.NewTable()
L.SetFuncs(t, api)
L.Push(t)
return 1
}

75
vendor/layeh.com/gopher-json/json_test.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
package json // import "layeh.com/gopher-json"
import (
"testing"
"github.com/yuin/gopher-lua"
)
func TestSimple(t *testing.T) {
const str = `
local json = require("json")
assert(type(json) == "table")
assert(type(json.decode) == "function")
assert(type(json.encode) == "function")
assert(json.encode(true) == "true")
assert(json.encode(1) == "1")
assert(json.encode(-10) == "-10")
assert(json.encode(nil) == "{}")
local obj = {"a",1,"b",2,"c",3}
local jsonStr = json.encode(obj)
local jsonObj = json.decode(jsonStr)
for i = 1, #obj do
assert(obj[i] == jsonObj[i])
end
local obj = {name="Tim",number=12345}
local jsonStr = json.encode(obj)
local jsonObj = json.decode(jsonStr)
assert(obj.name == jsonObj.name)
assert(obj.number == jsonObj.number)
local obj = {"a","b",what="c",[5]="asd"}
local jsonStr = json.encode(obj)
local jsonObj = json.decode(jsonStr)
assert(obj[1] == jsonObj["1"])
assert(obj[2] == jsonObj["2"])
assert(obj.what == jsonObj["what"])
assert(obj[5] == jsonObj["5"])
assert(json.decode("null") == nil)
assert(json.decode(json.encode({person={name = "tim",}})).person.name == "tim")
local obj = {
abc = 123,
def = nil,
}
local obj2 = {
obj = obj,
}
obj.obj2 = obj2
assert(json.encode(obj) == nil)
`
s := lua.NewState()
Preload(s)
if err := s.DoString(str); err != nil {
t.Error(err)
}
}
func TestCustomRequire(t *testing.T) {
const str = `
local j = require("JSON")
assert(type(j) == "table")
assert(type(j.decode) == "function")
assert(type(j.encode) == "function")
`
s := lua.NewState()
s.PreloadModule("JSON", Loader)
if err := s.DoString(str); err != nil {
t.Error(err)
}
}

111
vendor/layeh.com/gopher-json/util.go generated vendored Normal file
View File

@ -0,0 +1,111 @@
package json // import "layeh.com/gopher-json"
import (
"encoding/json"
"errors"
"strconv"
"github.com/yuin/gopher-lua"
)
var (
errFunction = errors.New("cannot encode function to JSON")
errChannel = errors.New("cannot encode channel to JSON")
errState = errors.New("cannot encode state to JSON")
errUserData = errors.New("cannot encode userdata to JSON")
errNested = errors.New("cannot encode recursively nested tables to JSON")
)
type jsonValue struct {
lua.LValue
visited map[*lua.LTable]bool
}
func (j jsonValue) MarshalJSON() ([]byte, error) {
return toJSON(j.LValue, j.visited)
}
func toJSON(value lua.LValue, visited map[*lua.LTable]bool) (data []byte, err error) {
switch converted := value.(type) {
case lua.LBool:
data, err = json.Marshal(converted)
case lua.LChannel:
err = errChannel
case lua.LNumber:
data, err = json.Marshal(converted)
case *lua.LFunction:
err = errFunction
case *lua.LNilType:
data, err = json.Marshal(converted)
case *lua.LState:
err = errState
case lua.LString:
data, err = json.Marshal(converted)
case *lua.LTable:
var arr []jsonValue
var obj map[string]jsonValue
if visited[converted] {
panic(errNested)
}
visited[converted] = true
converted.ForEach(func(k lua.LValue, v lua.LValue) {
i, numberKey := k.(lua.LNumber)
if numberKey && obj == nil {
index := int(i) - 1
if index != len(arr) {
// map out of order; convert to map
obj = make(map[string]jsonValue)
for i, value := range arr {
obj[strconv.Itoa(i+1)] = value
}
obj[strconv.Itoa(index+1)] = jsonValue{v, visited}
return
}
arr = append(arr, jsonValue{v, visited})
return
}
if obj == nil {
obj = make(map[string]jsonValue)
for i, value := range arr {
obj[strconv.Itoa(i+1)] = value
}
}
obj[k.String()] = jsonValue{v, visited}
})
if obj != nil {
data, err = json.Marshal(obj)
} else {
data, err = json.Marshal(arr)
}
case *lua.LUserData:
// TODO: call metatable __tostring?
err = errUserData
}
return
}
func fromJSON(L *lua.LState, value interface{}) lua.LValue {
switch converted := value.(type) {
case bool:
return lua.LBool(converted)
case float64:
return lua.LNumber(converted)
case string:
return lua.LString(converted)
case []interface{}:
arr := L.CreateTable(len(converted), 0)
for _, item := range converted {
arr.Append(fromJSON(L, item))
}
return arr
case map[string]interface{}:
tbl := L.CreateTable(0, len(converted))
for key, item := range converted {
tbl.RawSetH(lua.LString(key), fromJSON(L, item))
}
return tbl
}
return lua.LNil
}