Module:Luaon
跳到导航
跳到搜索
--[[
本模块用于生成和解码mw.logObject式的标记语言Luaon,同时发布于模块Wiki(luamodule.miraheze.org)。
所有状态储存在迭代器中。
由User:杨哲思从本地编辑器上复制得来,因此下面的所有缩进都是空格,没有使用tab。
]]
local luaon = {}
--[[
local G_ = {[_G]='$_G'}
luaon.G = G_
local function mirror(t,prefix)
for k,v in pairs(t) do
if type(k)=='string'
and (type(v)=='table'
or type(v)=='function')
then
if type(v)=='table' then
if not G_[v] then mirror(v,prefix..k..'.') end
if not G_[v] or #G_[v]>#(prefix..k) then G_[v] = prefix..k end
else
if not G_[v] or #G_[v]>#(prefix..k) then G_[v] = prefix..k end
end
end
end
end
mirror(_G,'$')
-- ]]
local ctrls = {
['\\n'] = '\n',
['\\\\'] = '\\',
['\\"'] = '"',
["\\'"] = "'"
}
local function encode(value,tables)
local t = type(value)
tables = tables or {len = 0}
if t == 'string' then
for k, v in pairs(ctrls) do
value = value:gsub(v, k)
end
return string.format("'%s'",value)
elseif t == 'number' then
return tostring(value)
elseif t == 'nil' then
return 'nil'
elseif t == 'boolean' then
return tostring(value)
end
--if G_[value] then
--return G_[value]
--end
if t == 'table' then
if tables[value] then
return '#'..tables[value]
end
tables[value] = tables.len + 1
tables.len = tables.len + 1
local output = '#'..tostring(tables[value])..'{'
for k, v in pairs(value) do
output = string.format('%s[%s]=%s,',output,encode(k,tables),encode(v,tables))
end
return output .. '}'
else
error('Unexpected type:'..t)
end
end
local decode, tonum, tostr, tobool, totable, field, tovalue, space, toglobal
local function reader(str)
local i = 0
return function(l)
if i + 1 > #str then return nil end
if type(l)=='number' then
i = i + l
return str:sub(i-l+1, i)
elseif l then
return str:sub(i+1,i+1),i
else
i = i + 1
return str:sub(i,i), str:sub(i+1,i+1), i
end
end
end
local function errorat(pos,char)
error(string.format('Error at pos %s \'%s\'.',pos,char))
end
function decode(str)
if type(str)~='string' then error('Luaon must be string') end
local reading = reader(str)
if str=="" then return end
return tovalue(reading)
end
function tovalue(reading,tables)
space(reading)
tables = tables or {}
local char, pos = reading(true)
if char=='#' then
return totable(reading,tables)
elseif char:match '[%d%-]' then
return tonum(reading)
elseif char=='"' or char=="'" then
return tostr(reading)
elseif char=='t' or char=='f' or char=='n' then
return tobool(reading)
elseif char=='$' then
return toglobal(reading)
else
error('unknown error at '..pos..char)
end
end
function tonum(reading)
local result = ''
local c, p
for char, nchar, pos in reading do
result = result .. char
if not nchar:match '[%d%.eE]' then
c,p = char,pos
break
end
end
result = tonumber(result)
if result then
return result
else
error('Unknown error when scanning number at '..p.."'"..c.."'.")
end
end
function tostr(reading)
local start = reading()
local str = ''
for char, nchar, pos in reading do
if char=='\\' then
str = str..(ctrls[char..(reading())] or char..nchar)
elseif char==start then
return str
else
str = str .. char
end
if not nchar then break end
end
error('unfinished string "'..str..'"')
end
function tobool(reading)
local bool ,_,pos= reading()
bool = bool .. reading(2)
if bool=="nil" then return nil end
bool = bool .. (reading())
if bool=="true" then return true end
bool = bool .. (reading())
if bool=="false" then return false end
errorat(pos,bool)
end
function totable(reading,tables)
reading()
local id = tonum(reading)
local result
local index = 1
if reading(true)=='{' then
result = {}
tables[id] = result
for char, nchar, pos in reading do
if char=='{' or char==',' then
local key, value
key, value, index = field(reading,tables,index)
if key~=nil then result[key] = value end
elseif char=='}' then
return result
else
error('Error at '.. pos .."'"..char .."'.")
end
end
else
return tables[id] or error('Table not declared')
end
end
local keyword = {
['do']=true,
['end']=true,
['function']=true,
['if']=true,
['then']=true,
['elseif']=true,
['else']=true,
['local']=true,
['while']=true,
['for']=true,
['in']=true,
['goto']=true,
['break']=true,
['repeat']=true,
['until']=true,
['not']=true,
['and']=true,
['or']=true,
['true']=true,
['false']=true,
['nil']=true,
['return']=true
}
function field(reading,tables,index)
space(reading)
local first = reading(true)
local key, value
space(reading)
if first == '[' then
local _,__,pos = reading()
key = tovalue(reading,tables)
if key==nil then error('Error at '..pos..':Table keys cannot be nil') end
space(reading)
local char, nchar, pos = reading()
if char~=']' then error(string.format("error at %s '%s'.",pos,char)) end
space(reading)
local char, nchar, pos = reading()
if char~='=' then error(string.format("error at %s '%s'.",pos,char)) end
space(reading)
value = tovalue(reading,tables)
space(reading)
elseif first:match '[%l_]' then
key = ""
local char,nchar,pos
repeat
char,nchar,pos = reading()
key = key..char
until nchar:match '%W'
if keyword[key] then error('Unexpected keyword at '.. pos .. " '".. key .."'.") end
space(reading)
char, nchar, pos = reading()
if char~='=' then error(string.format("error at %s '%s'.",pos,char)) end
space(reading)
value = tovalue(reading,tables)
space(reading)
elseif first=='}' or first==',' then
return nil
else
key = index
index = index + 1
value = tovalue(reading,tables)
space(reading)
end
return key, value, index
end
function toglobal(reading)
reading()
local char,nchar,pos
local key = ''
repeat
char,nchar,pos = reading()
key = key..char
until nchar:match '%W'
local global = _G
return global[key] or error('No such global var')
end
function space(reading)
while (reading(true)):match '%s' do
reading()
end
end
luaon.encode, luaon.decode = encode, decode
return luaon