// The CMLeonOS Project (https://github.com/Leonmmcoset/CMLeonOS)
// Copyright (C) 2025-present LeonOS 2 Developer Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
using System;
using System.Collections.Generic;
using System.Text;
using StringBuilder = System.Text.StringBuilder;
namespace UniLua
{
internal static class LuaJsonLib
{
public const string LIB_NAME = "json";
private static readonly string[] ESCAPE_CHARS = new string[]
{
"\"", "\\", "/", "\b", "\f", "\n", "\r", "\t"
};
public static int OpenLib(ILuaState lua)
{
NameFuncPair[] define = new NameFuncPair[]
{
new NameFuncPair("encode", Json_Encode),
new NameFuncPair("decode", Json_Decode),
new NameFuncPair("null", Json_Null),
new NameFuncPair("parse", Json_Parse),
new NameFuncPair("stringify", Json_Stringify)
};
lua.L_NewLib(define);
return 1;
}
private static int Json_Encode(ILuaState lua)
{
lua.L_CheckAny(1);
LuaType t = lua.Type(1);
if (t == LuaType.LUA_TNIL)
{
lua.PushString("null");
return 1;
}
if (t == LuaType.LUA_TBOOLEAN)
{
lua.PushString(lua.ToBoolean(1) ? "true" : "false");
return 1;
}
if (t == LuaType.LUA_TNUMBER)
{
lua.PushString(lua.ToNumber(1).ToString());
return 1;
}
if (t == LuaType.LUA_TSTRING)
{
string s = lua.ToString(1);
lua.PushString(JsonEscapeString(s));
return 1;
}
if (t == LuaType.LUA_TTABLE)
{
StringBuilder sb = new StringBuilder();
sb.Append("{");
bool first = true;
lua.PushNil();
while (lua.Next(1))
{
if (!first)
{
sb.Append(",");
}
first = false;
string key = lua.ToString(-2);
if (lua.Type(-1) == LuaType.LUA_TSTRING)
{
sb.Append("\"");
sb.Append(JsonEscapeString(key));
sb.Append("\":");
}
else
{
sb.Append(key);
sb.Append(":");
}
lua.PushValue(-1);
int result = Json_Encode(lua);
lua.Pop(1);
if (lua.Type(-1) == LuaType.LUA_TSTRING)
{
sb.Append("\"");
sb.Append(JsonEscapeString(lua.ToString(-1)));
}
else
{
sb.Append(lua.ToString(-1));
}
}
sb.Append("}");
lua.PushString(sb.ToString());
return 1;
}
lua.PushNil();
return 1;
}
private static int Json_Decode(ILuaState lua)
{
lua.L_CheckString(1);
string json = lua.ToString(1).Trim();
if (json == "null" || json.Length == 0)
{
lua.PushNil();
return 1;
}
if (json == "true")
{
lua.PushBoolean(true);
return 1;
}
if (json == "false")
{
lua.PushBoolean(false);
return 1;
}
if ((json.StartsWith("\"") && json.EndsWith("\"")) ||
(json.StartsWith("'") && json.EndsWith("'")))
{
string unquoted = json.Substring(1, json.Length - 2);
lua.PushString(unquoted);
return 1;
}
if (json.StartsWith("{") || json.StartsWith("["))
{
try
{
int pos = 0;
object result = ParseJson(json, ref pos);
PushLuaValue(lua, result);
return 1;
}
catch
{
lua.PushNil();
return 1;
}
}
if (char.IsDigit(json[0]))
{
double num = double.Parse(json);
lua.PushNumber(num);
return 1;
}
lua.PushNil();
return 1;
}
private static object ParseJson(string json, ref int pos)
{
json = json.Trim();
if (json.StartsWith("{"))
{
return ParseJsonObject(json, ref pos);
}
else if (json.StartsWith("["))
{
return ParseJsonArray(json, ref pos);
}
return null;
}
private static Dictionary ParseJsonObject(string json, ref int pos)
{
Dictionary dict = new Dictionary();
string content = json.Substring(1, json.Length - 2);
int contentPos = 0;
while (contentPos < content.Length)
{
contentPos = SkipWhitespace(content, contentPos);
if (contentPos >= content.Length)
{
break;
}
if (content[contentPos] == '}')
{
contentPos++;
break;
}
string key = ParseJsonString(content, ref contentPos);
contentPos = SkipWhitespace(content, contentPos);
if (contentPos >= content.Length || content[contentPos] != ':')
{
break;
}
contentPos++;
object value = ParseJsonValue(content, ref contentPos);
if (key != null)
{
dict[key] = value;
}
contentPos = SkipWhitespace(content, contentPos);
if (contentPos < content.Length && content[contentPos] == ',')
{
contentPos++;
}
}
pos = contentPos;
return dict;
}
private static List