The H Speed Guide to Lua (The H)
The H Speed Guide to Lua (The H)
Posted Apr 18, 2012 19:17 UTC (Wed) by wahern (subscriber, #37304)Parent article: The H Speed Guide to Lua (The H)
Whoever says Lua is too simplistic and verbose to do real work is kidding themselves. Here's a complete and very performant RFC4627 JSON parser in Lua and LPeg. While LPeg is technically written in C, it maintains all of its data structures (stacks, lists, strings, etc) within Lua. Although this code could be improved upon (the unicode string builder is 1/3 of the overall code and could easily be reduced and refactored without the bit32 library), I basically gave up after my first try as it works beautifully and outperformed everything I tested it against. This was also one of my first attempts at doing anything sophisticated with LPeg.
I've written many server applications using Lua, and it's easy to destroy Python and Perl in performance, even with far simpler and cleaner code. Every little task seems like it's more difficult with Lua, but when you put the finished product together inevitably the application is faster and cleaner.
local lpeg = require("lpeg")
local bit32 = bit32 or require("bit")
local function utf8(cap)
local code = tonumber(cap, 16)
local c1, c2, c3
if bit32.band(code, 0xF800) ~= 0 then
c1 = bit32.bor(0xE0, bit32.band(0x0F, bit32.rshift(code, 12)))
c2 = bit32.bor(0x80, bit32.band(0x3F, bit32.rshift(code, 6)))
c3 = bit32.bor(0x80, bit32.band(0x3F, code))
return string.char(c1, c2, c3)
elseif bit32.band(code, 0x0780) ~= 0 then
c1 = bit32.bor(0xC0, bit32.band(0x1F, bit32.rshift(code, 6)))
c2 = bit32.bor(0x80, bit32.band(0x3F, code))
return string.char(c1, c2)
else
return string.char(code)
end
end -- utf8
local xdigit = lpeg.R("09", "AF", "af")
local unicode = lpeg.P"u" * ((xdigit * xdigit * xdigit * xdigit) / utf8)
local escaped = (lpeg.P'"' * lpeg.Cc('"'))
+ (lpeg.P"\\" * lpeg.Cc("\\"))
+ (lpeg.P"/" * lpeg.Cc("/"))
+ (lpeg.P"b" * lpeg.Cc("\b"))
+ (lpeg.P"f" * lpeg.Cc("\f"))
+ (lpeg.P"n" * lpeg.Cc("\n"))
+ (lpeg.P"r" * lpeg.Cc("\r"))
+ (lpeg.P"t" * lpeg.Cc("\t"))
local encoded = lpeg.P"\\" * (unicode + escaped)
local regular = lpeg.C((-lpeg.S'\\"' * lpeg.P(1)))
local string = lpeg.Ct(lpeg.P'"' * (regular + encoded)^0 * lpeg.P'"') / function (t) return table.concat(t) end
local number = ((lpeg.S"+-"^-1 * lpeg.R"09"^1 * lpeg.P(lpeg.P"." * lpeg.R"09"^1)^-1 *
lpeg.P(lpeg.S"eE" * lpeg.S"+-"^-1 * lpeg.R"09"^1)^-1)
/ function (cap) return tonumber(cap) end)
local boolean = ((lpeg.P"true" * lpeg.Cc(true)) + (lpeg.P"false" * lpeg.Cc(false)))
local null = lpeg.P"null" * lpeg.Cc(nil)
local simple = string + number + boolean + null
local space = lpeg.S(" \t\r\n")^0
local Key, Value, Object, Array, Simple = lpeg.V"Key", lpeg.V"Value", lpeg.V"Object", lpeg.V"Array", lpeg.V"Simple"
local Grammar = lpeg.P{ "Value",
Key = space * string * space,
Value = space * (Object + Array + Simple) * space,
Array = lpeg.Ct("[" * (Value * lpeg.P","^-1)^0 * space * "]"),
Object = lpeg.Cf(lpeg.Ct"{" * lpeg.Cg(Key * lpeg.P":" * Value * lpeg.P","^-1)^0 * space * "}", rawset),
Simple = simple,
}
local function decode(string)
return lpeg.match(Grammar, string)
end
(Log in to post comments)
