Last active
March 4, 2019 07:12
-
-
Save hugeblank/3779132d9934c1ff6198c18ce8be2c91 to your computer and use it in GitHub Desktop.
Requirable form of ElvishJerricco's JSON parsing API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Requirable form of ElvishJerricco's JSON parsing API | |
Original: http://pastebin.com/4nRg9CHU | |
]] | |
local this = {} | |
------------------------------------------------------------------ utils | |
local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\"} | |
local function isArray(t) | |
local max = 0 | |
for k,v in pairs(t) do | |
if type(k) ~= "number" then | |
return false | |
elseif k > max then | |
max = k | |
end | |
end | |
return max == #t | |
end | |
local whites = {['\n']=true; ['\r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true} | |
this.removeWhite = function(str) | |
while whites[str:sub(1, 1)] do | |
str = str:sub(2) | |
end | |
return str | |
end | |
------------------------------------------------------------------ encoding | |
local function encodeCommon(val, pretty, tabLevel, tTracking) | |
local str = "" | |
-- Tabbing util | |
local function tab(s) | |
str = str .. ("\t"):rep(tabLevel) .. s | |
end | |
local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc) | |
str = str .. bracket | |
if pretty then | |
str = str .. "\n" | |
tabLevel = tabLevel + 1 | |
end | |
for k,v in iterator(val) do | |
tab("") | |
loopFunc(k,v) | |
str = str .. "," | |
if pretty then str = str .. "\n" end | |
end | |
if pretty then | |
tabLevel = tabLevel - 1 | |
end | |
if str:sub(-2) == ",\n" then | |
str = str:sub(1, -3) .. "\n" | |
elseif str:sub(-1) == "," then | |
str = str:sub(1, -2) | |
end | |
tab(closeBracket) | |
end | |
-- Table encoding | |
if type(val) == "table" then | |
assert(not tTracking[val], "Cannot encode a table holding itself recursively") | |
tTracking[val] = true | |
if isArray(val) then | |
arrEncoding(val, "[", "]", ipairs, function(k,v) | |
str = str .. encodeCommon(v, pretty, tabLevel, tTracking) | |
end) | |
else | |
arrEncoding(val, "{", "}", pairs, function(k,v) | |
assert(type(k) == "string", "JSON object keys must be strings", 2) | |
str = str .. encodeCommon(k, pretty, tabLevel, tTracking) | |
str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking) | |
end) | |
end | |
-- String encoding | |
elseif type(val) == "string" then | |
str = '"' .. val:gsub("[%c\"\\]", controls) .. '"' | |
-- Number encoding | |
elseif type(val) == "number" or type(val) == "boolean" then | |
str = tostring(val) | |
else | |
error("JSON only supports arrays, objects, numbers, booleans, and strings", 2) | |
end | |
return str | |
end | |
this.encode = function(val) | |
return encodeCommon(val, false, 0, {}) | |
end | |
this.encodePretty = function(val) | |
return encodeCommon(val, true, 0, {}) | |
end | |
------------------------------------------------------------------ decoding | |
local decodeControls = {} | |
for k,v in pairs(controls) do | |
decodeControls[v] = k | |
end | |
this.parseBoolean = function(str) | |
if str:sub(1, 4) == "true" then | |
return true, this.removeWhite(str:sub(5)) | |
else | |
return false, this.removeWhite(str:sub(6)) | |
end | |
end | |
this.parseNull = function(str) | |
return nil, this.removeWhite(str:sub(5)) | |
end | |
local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true} | |
this.parseNumber = function(str) | |
local i = 1 | |
while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do | |
i = i + 1 | |
end | |
local val = tonumber(str:sub(1, i - 1)) | |
str = this.removeWhite(str:sub(i)) | |
return val, str | |
end | |
this.parseString = function(str) | |
str = str:sub(2) | |
local s = "" | |
while str:sub(1,1) ~= "\"" do | |
local next = str:sub(1,1) | |
str = str:sub(2) | |
assert(next ~= "\n", "Unclosed string") | |
if next == "\\" then | |
local escape = str:sub(1,1) | |
str = str:sub(2) | |
next = assert(decodeControls[next..escape], "Invalid escape character") | |
end | |
s = s .. next | |
end | |
return s, this.removeWhite(str:sub(2)) | |
end | |
this.parseArray = function(str) | |
str = this.removeWhite(str:sub(2)) | |
local val = {} | |
local i = 1 | |
while str:sub(1, 1) ~= "]" do | |
local v = nil | |
v, str = this.parseValue(str) | |
val[i] = v | |
i = i + 1 | |
str = this.removeWhite(str) | |
end | |
str = this.removeWhite(str:sub(2)) | |
return val, str | |
end | |
this.parseObject = function(str) | |
str = this.removeWhite(str:sub(2)) | |
local val = {} | |
while str:sub(1, 1) ~= "}" do | |
local k, v = nil, nil | |
k, v, str = this.parseMember(str) | |
val[k] = v | |
str = this.removeWhite(str) | |
end | |
str = this.removeWhite(str:sub(2)) | |
return val, str | |
end | |
this.parseMember = function(str) | |
local k = nil | |
k, str = this.parseValue(str) | |
local val = nil | |
val, str = this.parseValue(str) | |
return k, val, str | |
end | |
this.parseValue = function(str) | |
local fchar = str:sub(1, 1) | |
if fchar == "{" then | |
return this.parseObject(str) | |
elseif fchar == "[" then | |
return this.parseArray(str) | |
elseif tonumber(fchar) ~= nil or numChars[fchar] then | |
return this.parseNumber(str) | |
elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then | |
return this.parseBoolean(str) | |
elseif fchar == "\"" then | |
return this.parseString(str) | |
elseif str:sub(1, 4) == "null" then | |
return this.parseNull(str) | |
end | |
return nil | |
end | |
this.decode = function(str) | |
str = this.removeWhite(str) | |
t = this.parseValue(str) | |
return t | |
end | |
this.decodeFromFile = function(path) | |
local file = assert(fs.open(path, "r")) | |
local decoded = this.decode(file.readAll()) | |
file.close() | |
return decoded | |
end | |
return this |
Sorry for the 5 extra changes that do nothing I just realized that this damn thing uses 8 spaces instead of 4
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please for the love of everything that is good, if you use this API, credit ElvishJerricco, not me.