View difference between Paste ID: eLyEnF5Q and ee3nwfpN
SHOW: | | - or go back to the newest paste.
1
------------------------------------------------------------------ utils
2
local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\"}
3
 
4
local function isArray(t)
5
    local max = 0
6
    for k,v in pairs(t) do
7
        if type(k) ~= "number" then
8
            return false
9
        elseif k > max then
10
            max = k
11
        end
12
    end
13
    return max == #t
14
end
15
 
16
local whites = {['\n']=true; ['r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true}
17
function removeWhite(str)
18
    while whites[str:sub(1, 1)] do
19
        str = str:sub(2)
20
    end
21
    return str
22
end
23
 
24
------------------------------------------------------------------ encoding
25
 
26
local function encodeCommon(val, pretty, tabLevel, tTracking)
27
    local str = ""
28
 
29
    -- Tabbing util
30
    local function tab(s)
31
        str = str .. ("\t"):rep(tabLevel) .. s
32
    end
33
 
34
    local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc)
35
        str = str .. bracket
36
        if pretty then
37
            str = str .. "\n"
38
            tabLevel = tabLevel + 1
39
        end
40
        for k,v in iterator(val) do
41
            tab("")
42
            loopFunc(k,v)
43
            str = str .. ","
44
            if pretty then str = str .. "\n" end
45
        end
46
        if pretty then
47
            tabLevel = tabLevel - 1
48
        end
49
        if str:sub(-2) == ",\n" then
50
            str = str:sub(1, -3) .. "\n"
51
        elseif str:sub(-1) == "," then
52
            str = str:sub(1, -2)
53
        end
54
        tab(closeBracket)
55
    end
56
 
57
    -- Table encoding
58
    if type(val) == "table" then
59
        assert(not tTracking[val], "Cannot encode a table holding itself recursively")
60
        tTracking[val] = true
61
        if isArray(val) then
62
            arrEncoding(val, "[", "]", ipairs, function(k,v)
63
                str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
64
            end)
65
        else
66
            arrEncoding(val, "{", "}", pairs, function(k,v)
67
                assert(type(k) == "string", "JSON object keys must be strings", 2)
68
                str = str .. encodeCommon(k, pretty, tabLevel, tTracking)
69
                str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking)
70
            end)
71
        end
72
    -- String encoding
73
    elseif type(val) == "string" then
74
        str = '"' .. val:gsub("[%c\"\\]", controls) .. '"'
75
    -- Number encoding
76
    elseif type(val) == "number" or type(val) == "boolean" then
77
        str = tostring(val)
78
    else
79
        error("JSON only supports arrays, objects, numbers, booleans, and strings", 2)
80
    end
81
    return str
82
end
83
 
84
function encode(val)
85
    return encodeCommon(val, false, 0, {})
86
end
87
 
88
function encodePretty(val)
89
    return encodeCommon(val, true, 0, {})
90
end
91
 
92
------------------------------------------------------------------ decoding
93
 
94
function parseBoolean(str)
95
    if str:sub(1, 4) == "true" then
96
        return true, removeWhite(str:sub(5))
97
    else
98
        return false, removeWhite(str:sub(6))
99
    end
100
end
101
 
102
function parseNull(str)
103
    return nil, removeWhite(str:sub(5))
104
end
105
 
106
local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true}
107
function parseNumber(str)
108
    local i = 1
109
    while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do
110
        i = i + 1
111
    end
112
    local val = tonumber(str:sub(1, i - 1))
113
    str = removeWhite(str:sub(i))
114
    return val, str
115
end
116
 
117
function parseString(str)
118
    local i,j = str:find('[^\\]"')
119
    local s = str:sub(2, j - 1)
120
 
121
    for k,v in pairs(controls) do
122
        s = s:gsub(v, k)
123
    end
124
    str = removeWhite(str:sub(j + 1))
125
    return s, str
126
end
127
 
128
function parseArray(str)
129
    str = removeWhite(str:sub(2))
130
   
131
    local val = {}
132
    local i = 1
133
    while str:sub(1, 1) ~= "]" do
134
        local v = nil
135
        v, str = parseValue(str)
136
        val[i] = v
137
        i = i + 1
138
        str = removeWhite(str)
139
    end
140
    str = removeWhite(str:sub(2))
141
    return val, str
142
end
143
 
144
function parseObject(str)
145
    str = removeWhite(str:sub(2))
146
   
147
    local val = {}
148
    while str:sub(1, 1) ~= "}" do
149
        local k, v = nil, nil
150
        k, v, str = parseMember(str)
151
        val[k] = v
152
        str = removeWhite(str)
153
    end
154
    str = removeWhite(str:sub(2))
155
    return val, str
156
end
157
 
158
function parseMember(str)
159
    local k = nil
160
    k, str = parseValue(str)
161
    local val = nil
162
    val, str = parseValue(str)
163
    return k, val, str
164
end
165
 
166
function parseValue(str)
167
    local fchar = str:sub(1, 1)
168
    if fchar == "{" then
169
        return parseObject(str)
170
    elseif fchar == "[" then
171
        return parseArray(str)
172
    elseif tonumber(fchar) ~= nil or numChars[fchar] then
173
        return parseNumber(str)
174
    elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then
175
        return parseBoolean(str)
176
    elseif fchar == "\"" then
177
        return parseString(str)
178
    elseif str:sub(1, 4) == "null" then
179
        return parseNull(str)
180
    end
181
    return nil
182
end
183
 
184
function decode(str)
185
    str = removeWhite(str)
186
    t = parseValue(str)
187
    return t
188
end
189
 
190
function decodeFromFile(path)
191
    local file = assert(fs.open(path, "r"))
192
    return decode(file.readAll())
193
end