这个魔兽世界插件输出一个EPGP。
我写了一个插件,将Lua数据转换为JSON对象,以便在公会网站上显示。它在旧版本的插件中工作,但现在我在试图让它正确转换文件时遇到了麻烦。下面是显示转换问题的两个代码片段——请看这个演示。
第一个用于形成嵌套数组:
["roster_info"] = {
{
"Agantica", -- [1]
"ROGUE", -- [2]
"09/03-2013", -- [3]
}, -- [1]
{
"Intikamim", -- [1]
"PALADIN", -- [2]
"17/02-2013", -- [3]
}, -- [2]
},
是
"roster_info" : [
[
"Agantica",
"ROGUE",
"09/03-2013"
],
[
"Intikamim",
"PALADIN",
"17/02-2013"
]
]
但是替换字符串将下一个代码片段视为嵌套数组,而它应该是数组内部的对象:
["bonus_loot_log"] = {
{
["player"] = "Magebox",
["timestamp"] = "2013-03-07 13:44:00",
["coinsLeft"] = "-1",
["reward"] = "|cffa335ee|Hitem:86815:0:0:0:0:0:0:632235520:90:0:445|h[Attenuating Bracers]|h|r",
}, -- [1]
{
["player"] = "Lîutasila",
["coinsLeft"] = "-1",
["timestamp"] = "2013-03-07 13:47:00",
}, -- [2]
},
是
"bonus_loot_log" : [
[
"player" : "Magebox",
"timestamp" : "2013-03-07 13:44:00",
"coinsLeft" : "-1",
"reward" : "|cffa335ee|Hitem:86815:0:0:0:0:0:0:632235520:90:0:445|h[Attenuating Bracers]|h|r"
],
[
"player": "Lîutasila",
"coinsLeft": "-1",
"timestamp": "2013-03-07 13:47:00"
]
]
这是只在第一个代码片段上起作用的字符串转换脚本。
lua_string
.replace(/[(.*)]s=s/g,'$1:') // change equal to colon & remove outer brackets
.replace(/[trn]/g,'') // remove tabs & returns
.replace(/},s--s[d+]}/g,']]') // replace sets ending with a comment with square brackets
.replace(/,s--s[d+]/g,',') // remove close subgroup and comment
.replace(/,(}|])/g,'$1') // remove trailing comma
.replace(/},{/g,'],[') // replace curly bracket set with square brackets
.replace(/{{/g,'[[') // change double curlies to square brackets
.replace(/EPGP_DBs=/,'');
所以,我需要一些帮助让Lua正确转换对象数组(第二个例子)。
一般不能简单地通过字符串操作将任何Lua表转换为JSON数据。问题是,虽然Lua对数组和字典都使用表,但JSON需要两种不同的类型。还有其他语法差异。
这是最好的解决模块之间的Lua和JSON表示转换。看看Lua wiki上的JSON模块,找到一个Lua模块来将Lua转换为JSON。有多个模块,其中一些是纯Lua,是嵌入《魔兽世界》的好选择。它们可以正确地检测表是否代表数组或字典,并输出相关的JSON。
可以通过语法解析稳定转换。然而,这是一个非常繁琐的过程。
$(function () {
$("#run").on("click", function () {
let src = $("#src").val();
let [i, contents] = convert(src, 0, []);
function isValue(element){
let idx = contents.indexOf(element) + 1
for(let i=idx; i < contents.length; i++){
if(["SPACE","TAB","RETURN"].indexOf(contents[i].type) > -1) continue;
if(contents[i].type == "SPLIT") return 0
if(contents[i].type == "BRKT_F") return 2
if(["BRKT_S","BRKT_W","BREAK","FBREAK"].indexOf(contents[i].type) > -1) return 1
}
}
let converted = "";
contents.forEach((element, index) => {
switch(element.type){
case "NUMBER":{
converted += element.content
break;
}
case "UNKNOWN": {
if(isValue(element)==1){
if(element.content == "return"){
} else if(["true","false"].indexOf(element.content)>-1){
converted += element.content
} else {
converted += '"' + element.content + '"'
}
} else if(isValue(element)==2){
converted += element.content
} else {
converted += '"' + element.content + '"'
}
break;
}
case "STR_S":
case "STR_D":{
converted += element.content
break;
}
case "BRKT_S":{
converted += element.content
break;
}
case "BRKT_W":{
converted += element.content
break;
}
case "BRKT_F":{
converted += element.content
break;
}
case "SPACE":{
converted += element.content
break;
}
case "TAB":{
converted += element.content
break;
}
case "RETURN":{
converted += element.content
break;
}
case "BREAK":{
converted += ","
break;
}
case "FBREAK":{
converted += "."
break;
}
case "SPLIT":{
converted += ":"
break;
}
}
});
$("#result").val(converted)
})
})
function getBracketSurfaceInner(contents, element){
if(["BRKT_S", "BRKT_W", "BRKT_F"].indexOf(element.type) == -1 || "]})".indexOf(element.content) == -1) return "";
let idx = contents.indexOf(element)
let innerElements = [];
let nest = 1;
for(let i=idx-1; i>=1; i--){
if(["BRKT_S", "BRKT_W", "BRKT_F" ].indexOf(contents[i].type)>=0){
if("]})".indexOf(contents[i].content)>=0){ nest ++ }
if("[{(".indexOf(contents[i].content)>=0){ nest -- }
}
if(nest==0 && contents[i].type == element.type){
return innerElements;
}
if(nest == 1) {
innerElements.unshift(contents[i]);
}
}
return innerElements;
}
function removeLastCamma(contents, element){
let idx = contents.indexOf(element)
let last = -1;
for(let i=idx-1; i>=1; i--){
if(["NUMBER", "UNKNOWN", "STR_S", "STR_D"].indexOf(contents[i].type)>=0) return;
if(contents[i].type == "BREAK"){
last = i;
break;
}
}
contents.splice(last, 1);
}
function convert(text, pos, contents) {
let MODE = undefined;
// NUMBER
// UNKNOWN
// SPLIT
// BREAK
// FBREAK
// STR_S
// STR_D
// BRKT_S
// BRKT_W
// BRKT_F
// CTRL
// RETURN
let MODES = [MODE];
let content = "", currentElement;
let i, c
function PUSH_BEFORE(replace) {
if (content.length > 1) {
contents.push({
type: MODE,
content: content.slice(0, content.length - 1),
});
}
content = "" + (replace ? replace : c)
currentElement = contents[contents.length-1];
MODE = MODES.shift()
}
function PUSH_AFTER(replace) {
if (content.length > 0) {
let str = (replace ? content.slice(0, content.length - 1) + replace : content.slice(0, content.length));
contents.push({
type: MODE,
content: str,
});
}
content = ""
currentElement = contents[contents.length-1];
MODE = MODES.shift()
}
for (i = pos; i < text.length; i++) {
c = text.charAt(i)
content = content + c
if (MODE == "ESCAPE") {
MODE = MODES.shift()
} else
if (MODE == "STR_S") {
if (c == "'") {
PUSH_AFTER('"')
}
} else
if (MODE == "STR_D") {
if (c == '"') {
PUSH_AFTER()
}
} else
if (MODE == "BRKT_S") {
if (c == ']') {
PUSH_BEFORE()
}
} else
if (MODE == "BRKT_F") {
if (c == ')') {
PUSH_BEFORE()
}
} else
if (MODE == "BRKT_W") {
if (c == '}') {
PUSH_BEFORE()
}
} else {
switch (c) {
case "{":{
PUSH_BEFORE()
MODE = "BRKT_W"
let begin_idx = contents.length;
contents.push({
type: MODE,
content: c,
});
MODES.push(MODE)
let [f, innerContents] = convert.call(this, text, i + 1, contents)
removeLastCamma(contents, contents[contents.length-1]);
let surface = getBracketSurfaceInner(innerContents, innerContents[innerContents.length-1]);
let d = 0;
for(let l=0; l<surface.length; l++){
if(surface[l].type == "SPLIT") { d = 1; break; }
}
i = f
content = ""
if(d==0){
contents[begin_idx].type = "BRKT_S";
contents[begin_idx].content = "[";
contents[contents.length-1].type = "BRKT_S";
contents[contents.length-1].content = "]";
MODE = MODES.shift() | "BRKT_S"
} else {
MODE = MODES.shift() | "BRKT_W"
}
break;
}
case "}":{
PUSH_BEFORE()
contents.push({
type: "BRKT_W",
content: c,
});
return [i, contents]
break;
}
case "[": {
PUSH_BEFORE()
MODE = "BRKT_S"
let begin_idx = contents.length;
contents.push({
type: MODE,
content: c,
});
MODES.push(MODE)
let [f, innerContents] = convert.call(this, text, i + 1, contents)
removeLastCamma(contents, contents[contents.length-1]);
innerContents = getBracketSurfaceInner(contents, contents[contents.length-1]);
let d = 0;
for(let l=0; l<innerContents.length; l++){
if(["BREAK", "BRKT_F"].indexOf(innerContents[l].type)>-1) {d = 1; break; }
}
if(d==0){
contents[begin_idx].type = "NOP";
contents[begin_idx].content = "";
contents[contents.length-1].type = "NOP";
contents[contents.length-1].content = "";
}
i = f
content = ""
MODE = MODES.shift() | "BRKT_S"
break;
}
case "]": {
PUSH_BEFORE()
contents.push({
type: "BRKT_S",
content: c,
});
return [i, contents]
break;
}
case "(": {
PUSH_BEFORE()
MODE = "BRKT_F"
let begin_idx = contents.length;
contents.push({
type: MODE,
content: c,
});
MODES.push(MODE)
let [f, innerContents] = convert.call(this, text, i + 1, contents)
removeLastCamma(contents, contents[contents.length-1]);
innerContents = getBracketSurfaceInner(contents, contents[contents.length-1]);
contents[begin_idx].type = "BRKT_F";
contents[begin_idx].content = "(";
contents[contents.length-1].type = "BRKT_F";
contents[contents.length-1].content = ")";
i = f
content = ""
MODE = MODES.shift() | "BRKT_F"
break;
}
case ")": {
PUSH_BEFORE()
contents.push({
type: "BRKT_F",
content: c,
});
return [i, contents]
break;
}
case "'": {
if(MODE=="STR_D") {
break;
}
PUSH_BEFORE('"')
MODE = "STR_S"
break;
}
case '"': {
if(MODE=="STR_S") {
break;
}
PUSH_BEFORE()
MODE = "STR_D"
break;
}
case "\": {
MODES.push(MODE)
MODE = "ESCAPE"
break;
}
case ",": {
PUSH_BEFORE()
MODE = "BREAK"
break;
}
case ".": {
PUSH_BEFORE()
MODE = "FBREAK"
break;
}
case "=": {
PUSH_BEFORE(":")
MODE = "SPLIT"
break;
}
case ":": {
PUSH_BEFORE()
MODE = "SPLIT"
break;
}
case " ": {
if (MODE != "SPACE") {
PUSH_BEFORE()
MODE = "SPACE"
}
break;
}
case "t": {
if (MODE != "TAB") {
PUSH_BEFORE()
MODE = "TAB"
}
break;
}
case "n": {
PUSH_BEFORE()
MODE = "RETURN"
break;
}
default: {
if (" SPACE TAB RETURN BREAK FBREAK SPLIT ".indexOf(" " + MODE + " ") > -1) {
PUSH_BEFORE()
}
if (!isNaN(content)) {
MODE = "NUMBER"
}
else {
MODE = "UNKNOWN"
}
break;
}
}
}
}
return [i, contents]
}
#src {
width: 400px;
height: 200px;
}
#result {
width: 400px;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Source:<br><textarea id="src"></textarea>
<button id="run">Convert</button><br>
Result:<br><textarea id="result"></textarea>
// convert EPGP_DB from LUA to JSON
var str = document.getElementsByTagName('data')[0].innerHTML;
var diff;
do { // replace curlies around arrays with square brackets
diff = str.length;
str = str.replace(/{(((nt*)t)S.*(2.*)*),s--s[d+]3}/g,'[$1$3]');
diff = diff - str.length;
} while (diff > 0);
str = str
.replace(/EPGP_DBs=s/, '') // remove variable definition
.replace(/s--s[d+](n)/g, '$1') // remove comment
.replace(/,(nt*})/g, '$1') // remove trailing comma
.replace(/[(.*?)]s=s/g,'$1:') // change equal to colon, remove brackets
.replace(/[trn]/g,''); // remove tabs & returns
console.log(str);
json = window.JSON.parse(str);
console.log(json);
document.getElementById('result').innerText = json.global.last_version;