Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
stringToTable() — Gideros Forum

stringToTable()

memememe Member
edited June 2015 in Code snippets
I had a loadstring("return " .. someStringFromServer)() in my game, which was there to receive lists of player names along with points etc. I found this too unsafe now. There's a few solutions to this, some override globals - blacklisting seems pretty unsafe, some try to parse and still use load(), so I wrote a very basic table parser.

It can handle simple strings; symbols nil, true, false; simple decimal numbers.
It *can't* handle code, escape sequences, anything else than decimal numbers (hexadecimal etc), scientific notation and more.

It's public domain.
function stringToTable(s)
	local t, side, key, negate, addDecimals = {}
	local nilFalse = true
	local count = 1
 
	local inQuotes
	local function changeInQuotes(quoteType)
		if not inQuotes then
			inQuotes = quoteType
			return true
		elseif inQuotes == quoteType then
			inQuotes = nil
			return true
		end
	end
 
	local function emit()
		local val
		if nilFalse then
			val = side
		else
			val = nilFalse
		end
 
		if key then
			t[key] = val
		else
			t[count] = val
			count = count + 1
		end
 
		side = nil
		nilFalse = true
		key = nil			
	end
 
	local function isChar(c)
		return c:byte(1) >= 65 and c:byte(1) <= 90
		or     c:byte(1) >= 97 and c:byte(1) <= 122
		or c == '_'
	end
 
	local function isDigit(c)
		return c:byte(1) >= 48 and c:byte(1) <= 57
	end
 
	local i = 2
	while i <= #s do
		local c = s:sub(i,i)
		if c ~= "'" or not changeInQuotes(1) then
		if c ~= '"' or not changeInQuotes(2) then
		if s:sub(i,i+2) == 'nil' then
			side = true
			nilFalse = nil
			i=i+2
		elseif s:sub(i,i+3) == 'true' then
			side = true
			i=i+3
		elseif s:sub(i,i+4) == 'false' then
			side = true
			nilFalse = false
			i=i+4
		elseif inQuotes or isChar(c) then
			side = (side or '') .. c
		elseif isDigit(c) then
			local digit = c:byte(1) - 48
			side = side or 0
			if addDecimals then
				side = side + digit * math.pow(10, -addDecimals)				
				addDecimals = addDecimals + 1
			else
				side = side * 10 + digit
			end
			local c2 = s:sub(i+1,i+1)
			if negate and not isDigit(c2) and c2 ~= '.' then
				side = -side
				negate = nil
			end
			if not isDigit(c2) then
				addDecimals = nil
			end
		elseif c == '-' then
			negate = true
		elseif c == '.' then
			addDecimals = 1
		elseif c == '{' then
			side, j = stringToTable(s:sub(i))
			i=i+j - 1
		elseif c == '}' then
			if side then emit() end
			return t, i
		elseif c == '=' then
			key = side
			side = nil
		elseif c == ',' then
			emit()
		elseif c == '[' then
		elseif c == ']' then
		elseif c == ' '
		    or c == '\r'
		    or c == '\n'
		    or c == '\t' then
		else
			return
		end
		end
		end
		i=i+1		
	end
end
 
function dumptable(t)
	local function maybeQuote(s)
		local quote = ""
		if type(s) == 'string' then
			return "'" .. tostring(s) .. "'"
		else
			return tostring(s)
		end
	end
	local result = '#==' .. #t .. ' {'
	for k,v in pairs(t) do
		local vv
		if type(v) == 'table' then vv = dumptable(v) else vv = maybeQuote(v) end
		result = result .. maybeQuote(k) .. '=' .. vv .. ', '
	end
	return result .. '}'
end
 
function test(s)
	print('load: ' .. dumptable(load("return " .. s)()))
	print('stot: ' .. dumptable(stringToTable(s)))
	print()
end
 
test"{nil}"
test"{true}"
test"{false}"
test"{[1]=1,[2]=2,[3]=3}"
test"{[1]='a',[2]='b',[3]='c'}"
test"{[1]='a',[3]='b',[5]='c'}"
test"{['1']='a',['3']='b',['5']='c'}"
test"{'abc','def'}"
test"{'abc','def',}"
test"{{'abc',123}}"
test"{{'abc',123.789, 456}}"
test"{{'abc',-123.789}}"
test"{{'abc',.789}}"
test"{{'abc',-.789}}"
test"{['abc']=true,['def']=true}"
test"{\r\n\t['abc']\r\n\t=\r\n\ttrue,\r\n\t[\r\n\t'def'\r\n\t]\r\n\t=\r\n\ttrue\r\n\t}"
test"{abckey='abcval',defkey='defval',nested={'nestedabc','nesteddef',123,nil,true,false}}"
test'{abckey="abcval",defkey="defval",nested={"nestedabc","nesteddef",123,nil,true,false}}'

Likes: ar2rsawseen, pie

+1 -1 (+2 / -0 )Share on Facebook

Comments

  • Can you change the structure of your structure coming from server ? if yes why dont' you use JSON ? it works quite nicely and handles all exception you just mentioned

    -uk
  • memememe Member
    It just replaces load() with simple tables. Didn't need anything else. If anyone needs the exceptions, they'll need something else, not the code snippet, that's why I mentioned them.
    Markus
Sign In or Register to comment.