| --- **LIBEZT-1.0** Is a simple library for importing/rendering EZT templates |
| -- @class file |
| -- @name LIBEZT-1.0 |
| |
| --[[ LIBEZT-1.0 |
| |
| Revision: $Rev: 1707563 $ |
| Author(s): Humbedooh |
| Description: EZT import library |
| License: Apache License, v/2.0 |
| |
| ]] |
| |
| local LIBEZT_MAJOR = "LibEZT-1.0" |
| -- version number of this file (automatically generated by SVN) |
| local LIBEZT_MINOR = tonumber(("$Rev: 1707563 $"):match("(%d+)")) or 10000; |
| local LIBEZT = false; |
| if LibStub then |
| LIBEZT = LibStub:NewLibrary(LIBEZT_MAJOR, LIBEZT_MINOR) |
| _G.LIBEZT = LIBEZT; |
| else |
| if not _G.LIBEZT or (type(_G.LIBEZT) == "table" and _G.LIBEZT.rev < LIBEZT_MINOR) then |
| _G.LIBEZT = {rev = LIBEZT_MINOR}; |
| end |
| LIBEZT = _G.LIBEZT; |
| end |
| if not LIBEZT then return end |
| |
| function LIBEZT:version() |
| return LIBEZT_MINOR |
| end |
| |
| function LIBEZT.parseargs(s) |
| local arg = {}; |
| for w in s:gmatch("([%w:%-]+)") do table.insert(arg,w) end |
| return arg; |
| end |
| |
| function LIBEZT.at(s,e) |
| local l = 1; |
| for n = 1, e do if s:sub(n,n)=="\n" then l=l+1 end end |
| return l |
| end |
| |
| function LIBEZT:import(s, loose) |
| local tremove, parseargs,tinsert = table.remove, LIBEZT.parseargs,table.insert; |
| local stack = {}; |
| local top = {}; |
| tinsert(stack, top); |
| local ni,c,class,args; |
| local i, j = 1, 1; |
| local ci, cj -- start and end of HTML comment (if any) |
| local searchComment = true -- whether there are comments still to find |
| while true do |
| -- class: (alpha | '_') (alphanum | '_' | '-')* |
| -- args = anything afterwards |
| ni,j,class,args = s:find("%[([%l%u_][%w%-_]*)([^%]]*)%]", i); |
| if not ni then break end |
| --[[ |
| HTML comments may contain constructs that look like ezt sytnax, e.g. |
| <!--[if lt IE 9]> ... <![endif]--> |
| |
| In order to support HTML comments, we need to scan for either a comment or an EZT command |
| However Lua does not support RE alternation, so this needs to be done as a separate search. |
| |
| Also Lua does not support a "continue" clause so we need to use a nested if |
| ]] |
| if searchComment then ci, cj = s:find("<!%-%-.-%-%->", i) end -- find next HTML comment (if any) |
| if not ci then searchComment = False end -- no comment found; there cannot be any more |
| if searchComment and ci < ni then -- the comment starts before the ezt command |
| top[#top+1]=s:sub(i, cj); |
| i = cj + 1 -- skip the comment |
| -- and continue to look for next ezt command |
| else |
| -- found an ezt command outside an HTML comment |
| local text = s:sub(i, ni-1); |
| if text:find("%S") then |
| top[#top+1]=text; |
| end |
| if (class ~= "ezt" and class ~= "end" and class ~= "if" and class~= "is" and class~= "if-any" and class ~= "define" and class~= "for") then -- empty element |
| top[#top+1] ={class=class, args=parseargs(args), empty=true, ni=ni}; |
| elseif class ~= "end" then -- start tag |
| -- print("open " .. class) |
| top = {class=class, args=parseargs(args), ni=ni}; |
| stack[#stack+1] = top; -- new level |
| elseif class == "end" then -- end tag |
| local toclose = tremove(stack); -- pop the top and get the opening tag |
| --print("close " .. toclose.class) |
| top = stack[#stack]; |
| if #stack < 1 then |
| -- print( ("%s: No opening tag found for <%s> (at line %u)!"):format("LibEZT", class, LIBEZT.at(s,ni))) |
| return nil, ("%s: No opening tag found for <%s> (at line %u)!"):format("LibEZT", class, LIBEZT.at(s,ni)); |
| end |
| top[#top+1] = toclose; |
| end |
| i = j+1; |
| end |
| end |
| local text = s:sub(i); |
| if not text:find("^%s*$") then |
| local st = stack[#stack]; |
| st[#st+1] = text; |
| end |
| if #stack > 1 then |
| local l = LIBEZT.at(s, (stack[#stack].ni or 0)) |
| print(("%s: EZT data ended without closing [%s] (at line %u)!"):format("LibEZT", stack[#stack].class, l)) |
| return nil, ("%s: EZT data ended without closing <%s> (at line %u)!"):format("LibEZT", stack[#stack].class, l); |
| end |
| return stack[1]; |
| end |
| |
| function LIBEZT.construct(self, child, defs) |
| if type(child) == "string" then |
| return child or "" |
| elseif type(child) == "table" and #table > 0 then |
| -- print("got a root") |
| local rv = "" |
| for k, v in pairs(child) do |
| print(k) -- TODO this looks like a debug statement, should it still be enabled? |
| rv = rv .. LIBEZT.construct(self, v, defs) |
| end |
| return rv |
| elseif child.class then |
| if child.class == "ezt" then |
| -- print("Starting EZT...") |
| local rv = "" |
| for k, v in ipairs(child) do |
| rv = rv .. LIBEZT.construct(self, v, defs) |
| end |
| return rv |
| elseif child.class == "define" then |
| -- print("got a define") |
| local key = child.args[1] |
| defs.strings[key] = child[1] |
| -- print("set", key, child[1]) |
| return "" |
| elseif child.class == "for" then |
| local rv = "" |
| -- print("Doing for...") |
| if child.args[1] and defs.arrays[child.args[1]] then |
| for x,y in pairs(defs.arrays[child.args[1]]) do |
| defs.strings[child.args[1]] = y |
| for k, v in ipairs(child) do |
| --print(k) |
| rv = rv .. LIBEZT.construct(self, v, defs) |
| end |
| end |
| end |
| return rv |
| elseif child.class == "if-any" then |
| local rv = "" |
| -- print("Doing if..") |
| if child.args[1] then |
| if defs.strings[child.args[1]] or defs.arrays[child.args[1]] then |
| for k, v in ipairs(child) do |
| rv = rv .. LIBEZT.construct(self, v, defs) |
| end |
| end |
| end |
| return rv |
| elseif child.class == "is" then |
| local rv = "" |
| -- print("Doing is..") |
| rv = "" |
| if #child.args == 2 then |
| local a = defs.strings[child.args[1]] or nil |
| local b = defs.strings[child.args[2]] or nil |
| if a == b then |
| for k, v in ipairs(child) do |
| rv = rv .. LIBEZT.construct(self, v, defs) |
| end |
| end |
| end |
| return rv |
| elseif child.empty then |
| --print(child.class) |
| return tostring(defs.strings[child.class] or "") |
| end |
| else |
| local rv = "" |
| for k, v in pairs(child) do |
| rv = rv .. LIBEZT.construct(self, v, defs) |
| end |
| return rv |
| end |
| end |
| |
| return LIBEZT |