| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 文件名:Json_Class.nut | 
					
						
							|  |  |  | 路径:Base/_Tool/Json_Class.nut | 
					
						
							|  |  |  | 创建日期:2024-09-27	23:54 | 
					
						
							|  |  |  | 文件用途:Json类 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class JSONParser { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static version = "1.0.1"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     state = ""; | 
					
						
							|  |  |  |     stack = null; | 
					
						
							|  |  |  |     container = null; | 
					
						
							|  |  |  |     key = ""; | 
					
						
							|  |  |  |     value = ""; | 
					
						
							|  |  |  |     converter = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor() { | 
					
						
							|  |  |  |         stack = []; | 
					
						
							|  |  |  |         container = {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function parse(str, ...) { | 
					
						
							|  |  |  |         if (vargc > 0) converter = vargc[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         local string = { | 
					
						
							|  |  |  |             go = function() { | 
					
						
							|  |  |  |                 state = "ok"; | 
					
						
							|  |  |  |             }.bindenv(this), | 
					
						
							|  |  |  |             firstokey = function() { | 
					
						
							|  |  |  |                 key = value; | 
					
						
							|  |  |  |                 state = "colon"; | 
					
						
							|  |  |  |             }.bindenv(this), | 
					
						
							|  |  |  |             okey = function() { | 
					
						
							|  |  |  |                 key = value; | 
					
						
							|  |  |  |                 state = "colon"; | 
					
						
							|  |  |  |             }.bindenv(this), | 
					
						
							|  |  |  |             ovalue = function() { | 
					
						
							|  |  |  |                 value = this._convert(value, "string", converter); | 
					
						
							|  |  |  |                 state = "ocomma"; | 
					
						
							|  |  |  |             }.bindenv(this), | 
					
						
							|  |  |  |             firstavalue = function() { | 
					
						
							|  |  |  |                 value = this._convert(value, "string", converter); | 
					
						
							|  |  |  |                 state = "acomma"; | 
					
						
							|  |  |  |             }.bindenv(this), | 
					
						
							|  |  |  |             avalue = function() { | 
					
						
							|  |  |  |                 value = value; | 
					
						
							|  |  |  |                 this._convert(value, "string", converter); | 
					
						
							|  |  |  |                 state = "acomma"; | 
					
						
							|  |  |  |             }.bindenv(this) | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         local number = { | 
					
						
							|  |  |  |             go = function() { | 
					
						
							|  |  |  |                 state = "ok"; | 
					
						
							|  |  |  |             }.bindenv(this), | 
					
						
							|  |  |  |             ovalue = function() { | 
					
						
							|  |  |  |                 value = this._convert(value, "number", converter); | 
					
						
							|  |  |  |                 state = "ocomma"; | 
					
						
							|  |  |  |             }.bindenv(this), | 
					
						
							|  |  |  |             firstavalue = function() { | 
					
						
							|  |  |  |                 value = this._convert(value, "number", converter); | 
					
						
							|  |  |  |                 state = "acomma"; | 
					
						
							|  |  |  |             }.bindenv(this), | 
					
						
							|  |  |  |             avalue = function() { | 
					
						
							|  |  |  |                 value = this._convert(value, "number", converter); | 
					
						
							|  |  |  |                 state = "acomma"; | 
					
						
							|  |  |  |             }.bindenv(this) | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         local action = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         action["{"] <- { | 
					
						
							|  |  |  |                 go = function() { | 
					
						
							|  |  |  |                     stack.push({ | 
					
						
							|  |  |  |                         state = "ok" | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     container = {}; | 
					
						
							|  |  |  |                     state = "firstokey"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 ovalue = function() { | 
					
						
							|  |  |  |                     stack.push({ | 
					
						
							|  |  |  |                         container = container, | 
					
						
							|  |  |  |                         state = "ocomma", | 
					
						
							|  |  |  |                         key = key | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     container = {}; | 
					
						
							|  |  |  |                     state = "firstokey"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 firstavalue = function() { | 
					
						
							|  |  |  |                     stack.push({ | 
					
						
							|  |  |  |                         container = container, | 
					
						
							|  |  |  |                         state = "acomma" | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     container = {}; | 
					
						
							|  |  |  |                     state = "firstokey"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 avalue = function() { | 
					
						
							|  |  |  |                     stack.push({ | 
					
						
							|  |  |  |                         container = container, | 
					
						
							|  |  |  |                         state = "acomma" | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     container = {}; | 
					
						
							|  |  |  |                     state = "firstokey"; | 
					
						
							|  |  |  |                 }.bindenv(this) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             action["}"] <- { | 
					
						
							|  |  |  |                 firstokey = function() { | 
					
						
							|  |  |  |                     local pop = stack.pop(); | 
					
						
							|  |  |  |                     value = container; | 
					
						
							|  |  |  |                     container = ("container" in pop) ? pop.container : null; | 
					
						
							|  |  |  |                     key = ("key" in pop) ? pop.key : null; | 
					
						
							|  |  |  |                     state = pop.state; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 ocomma = function() { | 
					
						
							|  |  |  |                     local pop = stack.pop(); | 
					
						
							|  |  |  |                     container[key] <- value; | 
					
						
							|  |  |  |                     value = container; | 
					
						
							|  |  |  |                     container = ("container" in pop) ? pop.container : null; | 
					
						
							|  |  |  |                     key = ("key" in pop) ? pop.key : null; | 
					
						
							|  |  |  |                     state = pop.state; | 
					
						
							|  |  |  |                 }.bindenv(this) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             action["["] <- { | 
					
						
							|  |  |  |                 go = function() { | 
					
						
							|  |  |  |                     stack.push({ | 
					
						
							|  |  |  |                         state = "ok" | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     container = []; | 
					
						
							|  |  |  |                     state = "firstavalue"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 ovalue = function() { | 
					
						
							|  |  |  |                     stack.push({ | 
					
						
							|  |  |  |                         container = container, | 
					
						
							|  |  |  |                         state = "ocomma", | 
					
						
							|  |  |  |                         key = key | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     container = []; | 
					
						
							|  |  |  |                     state = "firstavalue"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 firstavalue = function() { | 
					
						
							|  |  |  |                     stack.push({ | 
					
						
							|  |  |  |                         container = container, | 
					
						
							|  |  |  |                         state = "acomma" | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     container = []; | 
					
						
							|  |  |  |                     state = "firstavalue"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 avalue = function() { | 
					
						
							|  |  |  |                     stack.push({ | 
					
						
							|  |  |  |                         container = container, | 
					
						
							|  |  |  |                         state = "acomma" | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     container = []; | 
					
						
							|  |  |  |                     state = "firstavalue"; | 
					
						
							|  |  |  |                 }.bindenv(this) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             action["]"] <- { | 
					
						
							|  |  |  |                 firstavalue = function() { | 
					
						
							|  |  |  |                     local pop = stack.pop(); | 
					
						
							|  |  |  |                     value = container; | 
					
						
							|  |  |  |                     container = ("container" in pop) ? pop.container : null; | 
					
						
							|  |  |  |                     key = ("key" in pop) ? pop.key : null; | 
					
						
							|  |  |  |                     state = pop.state; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 acomma = function() { | 
					
						
							|  |  |  |                     local pop = stack.pop(); | 
					
						
							|  |  |  |                     container.push(value); | 
					
						
							|  |  |  |                     value = container; | 
					
						
							|  |  |  |                     container = ("container" in pop) ? pop.container : null; | 
					
						
							|  |  |  |                     key = ("key" in pop) ? pop.key : null; | 
					
						
							|  |  |  |                     state = pop.state; | 
					
						
							|  |  |  |                 }.bindenv(this) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             action[":"] <- { | 
					
						
							|  |  |  |                 colon = function() { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                     local err = false; | 
					
						
							|  |  |  |                     foreach(akey, avalue in container) { | 
					
						
							|  |  |  |                         if (akey == key) err = true; | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     if (err) throw "Duplicate key \"" + key + "\""; | 
					
						
							|  |  |  |                     state = "ovalue"; | 
					
						
							|  |  |  |                 }.bindenv(this) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             action[","] <- { | 
					
						
							|  |  |  |                 ocomma = function() { | 
					
						
							|  |  |  |                     container[key] <- value; | 
					
						
							|  |  |  |                     state = "okey"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 acomma = function() { | 
					
						
							|  |  |  |                     container.push(value); | 
					
						
							|  |  |  |                     state = "avalue"; | 
					
						
							|  |  |  |                 }.bindenv(this) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             action["true"] <- { | 
					
						
							|  |  |  |                 go = function() { | 
					
						
							|  |  |  |                     value = true; | 
					
						
							|  |  |  |                     state = "ok"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 ovalue = function() { | 
					
						
							|  |  |  |                     value = true; | 
					
						
							|  |  |  |                     state = "ocomma"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 firstavalue = function() { | 
					
						
							|  |  |  |                     value = true; | 
					
						
							|  |  |  |                     state = "acomma"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 avalue = function() { | 
					
						
							|  |  |  |                     value = true; | 
					
						
							|  |  |  |                     state = "acomma"; | 
					
						
							|  |  |  |                 }.bindenv(this) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             action["false"] <- { | 
					
						
							|  |  |  |                 go = function() { | 
					
						
							|  |  |  |                     value = false; | 
					
						
							|  |  |  |                     state = "ok"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 ovalue = function() { | 
					
						
							|  |  |  |                     value = false; | 
					
						
							|  |  |  |                     state = "ocomma"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 firstavalue = function() { | 
					
						
							|  |  |  |                     value = false; | 
					
						
							|  |  |  |                     state = "acomma"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 avalue = function() { | 
					
						
							|  |  |  |                     value = false; | 
					
						
							|  |  |  |                     state = "acomma"; | 
					
						
							|  |  |  |                 }.bindenv(this) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             action["null"] <- { | 
					
						
							|  |  |  |                 go = function() { | 
					
						
							|  |  |  |                     value = null; | 
					
						
							|  |  |  |                     state = "ok"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 ovalue = function() { | 
					
						
							|  |  |  |                     value = null; | 
					
						
							|  |  |  |                     state = "ocomma"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 firstavalue = function() { | 
					
						
							|  |  |  |                     value = null; | 
					
						
							|  |  |  |                     state = "acomma"; | 
					
						
							|  |  |  |                 }.bindenv(this), | 
					
						
							|  |  |  |                 avalue = function() { | 
					
						
							|  |  |  |                     value = null; | 
					
						
							|  |  |  |                     state = "acomma"; | 
					
						
							|  |  |  |                 }.bindenv(this) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         state = "go"; | 
					
						
							|  |  |  |         stack = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         local start = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             local result, token, tokenizer = _JSONTokenizer(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             while (token = tokenizer.nextToken(str, start)) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if ("ptfn" == token.type) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                     action[token.value][state](); | 
					
						
							|  |  |  |                 } else if ("number" == token.type) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                     value = token.value; | 
					
						
							|  |  |  |                     number[state](); | 
					
						
							|  |  |  |                 } else if ("string" == token.type) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                     value = tokenizer.unescape(token.value); | 
					
						
							|  |  |  |                     string[state](); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 start += token.length; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } catch (e) { | 
					
						
							|  |  |  |             state = e; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (state != "ok" || regexp("[^\\s]").capture(str, start)) { | 
					
						
							|  |  |  |             local near = str.slice(start, GetMin(str.len(), start + 10)); | 
					
						
							|  |  |  |             throw "JSON Syntax Error near `" + near + "`"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return value; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function GetMin(a, b) { | 
					
						
							|  |  |  |         return a< b ? a : b; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function _convert(value, type, converter) { | 
					
						
							|  |  |  |         if ("function" == typeof converter) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |             local parametercCount = 2; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |             if ("getinfos" in converter) { | 
					
						
							|  |  |  |                 parametercCount = converter.getinfos().parameters.len() - | 
					
						
							|  |  |  |                     1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (parametercCount == 1) { | 
					
						
							|  |  |  |                 return converter(value); | 
					
						
							|  |  |  |             } else if (parametercCount == 2) { | 
					
						
							|  |  |  |                 return converter(value, type); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 throw "Error: converter function must take 1 or 2 parameters" | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } else if ("number" == type) { | 
					
						
							|  |  |  |             local Ret = (value.find(".") == null && value.find("e") == null && value.find("E") == null) ? value.tointeger() : value.tofloat(); | 
					
						
							|  |  |  |             return Ret; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return value; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _JSONTokenizer { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _ptfnRegex = null; | 
					
						
							|  |  |  |     _numberRegex = null; | 
					
						
							|  |  |  |     _stringRegex = null; | 
					
						
							|  |  |  |     _ltrimRegex = null; | 
					
						
							|  |  |  |     _unescapeRegex = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor() { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         this._ptfnRegex = regexp("^(?:\\,|\\:|\\[|\\]|\\{|\\}|true|false|null)"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         this._numberRegex = regexp("^(?:\\-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         this._stringRegex = regexp("^(?:\\\"((?:[^\\r\\n\\t\\\\\\\"]|\\\\(?:[\"\\\\\\/trnfb]|u[0-9a-fA-F]{4}))*)\\\")"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         this._ltrimRegex = regexp("^[\\s\\t\\n\\r]*"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         this._unescapeRegex = regexp("\\\\(?:(?:u\\d{4})|[\\\"\\\\/bfnrt])"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function nextToken(str, ...) { | 
					
						
							|  |  |  |         local start = 0; | 
					
						
							|  |  |  |         if (vargc > 0) start = vargv[0]; | 
					
						
							|  |  |  |         local | 
					
						
							|  |  |  |         m, | 
					
						
							|  |  |  |         type, | 
					
						
							|  |  |  |         token, | 
					
						
							|  |  |  |         value, | 
					
						
							|  |  |  |         length, | 
					
						
							|  |  |  |         whitespaces; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         whitespaces = this._leadingWhitespaces(str, start); | 
					
						
							|  |  |  |         start += whitespaces; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (m = this._ptfnRegex.capture(str, start)) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |             value = str.slice(m[0].begin, m[0].end); | 
					
						
							|  |  |  |             type = "ptfn"; | 
					
						
							|  |  |  |         } else if (m = this._numberRegex.capture(str, start)) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |             value = str.slice(m[0].begin, m[0].end); | 
					
						
							|  |  |  |             type = "number"; | 
					
						
							|  |  |  |         } else if (m = this._stringRegex.capture(str, start)) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |             value = str.slice(m[1].begin, m[1].end); | 
					
						
							|  |  |  |             type = "string"; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         token = { | 
					
						
							|  |  |  |             type = type, | 
					
						
							|  |  |  |             value = value, | 
					
						
							|  |  |  |             length = m[0].end - m[0].begin + whitespaces | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return token; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function _leadingWhitespaces(str, start) { | 
					
						
							|  |  |  |         local r = this._ltrimRegex.capture(str, start); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (r) { | 
					
						
							|  |  |  |             return r[0].end - r[0].begin; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |     _unescapeReplacements = { | 
					
						
							|  |  |  |         b = "\b", | 
					
						
							|  |  |  |         f = "\f", | 
					
						
							|  |  |  |         n = "\n", | 
					
						
							|  |  |  |         r = "\r", | 
					
						
							|  |  |  |         t = "\t" | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function unescape(str) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         local start = 0; | 
					
						
							|  |  |  |         local res = ""; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         while (start< str.len()) { | 
					
						
							|  |  |  |             local m = this._unescapeRegex.capture(str, start); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (m) { | 
					
						
							|  |  |  |                 local token = str.slice(m[0].begin, m[0].end); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                 local pre = str.slice(start, m[0].begin); | 
					
						
							|  |  |  |                 res += pre; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (token.len() == 6) { | 
					
						
							|  |  |  |                     res += token; | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     local char = token.slice(1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if (char in this._unescapeReplacements) { | 
					
						
							|  |  |  |                         res += this._unescapeReplacements[char]; | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         res += char; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 res += str.slice(start); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             start = m[0].end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class JSONEncoder { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static VERSION = "2.0.0"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |     static _maxDepth = 32; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function encode(value) { | 
					
						
							|  |  |  |         return this._encode(value); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function _encode(val, ...) { | 
					
						
							|  |  |  |         local depth = 0; | 
					
						
							|  |  |  |         if (vargc > 0) depth = vargv[0]; | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |         if (depth > this._maxDepth) { | 
					
						
							|  |  |  |             throw "Possible cyclic reference"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         local | 
					
						
							|  |  |  |         r = "", | 
					
						
							|  |  |  |             s = "", | 
					
						
							|  |  |  |             i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         switch (typeof val) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             case "table": | 
					
						
							|  |  |  |             case "class": | 
					
						
							|  |  |  |                 s = ""; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                 foreach(k, v in val) { | 
					
						
							|  |  |  |                     if (typeof v != "function") { | 
					
						
							|  |  |  |                         s += ",\"" + k + "\":" + this._encode(v, depth + 1); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 s = s.len() > 0 ? s.slice(1) : s; | 
					
						
							|  |  |  |                 r += "{" + s + "}"; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             case "array": | 
					
						
							|  |  |  |                 s = ""; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for (i = 0; i< val.len(); i++) { | 
					
						
							|  |  |  |                     s += "," + this._encode(val[i], depth + 1); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 s = (i > 0) ? s.slice(1) : s; | 
					
						
							|  |  |  |                 r += "[" + s + "]"; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             case "integer": | 
					
						
							|  |  |  |             case "float": | 
					
						
							|  |  |  |             case "bool": | 
					
						
							|  |  |  |                 r += val; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             case "null": | 
					
						
							|  |  |  |                 r += "null"; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             case "instance": | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if ("_serializeRaw" in val && typeof val._serializeRaw == "function") { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                     r += val._serializeRaw().tostring(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 } else if ("_serialize" in val && typeof val._serialize == "function") { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                     r += this._encode(val._serialize(), depth + 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     s = ""; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     try { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                         foreach(k, v in val) { | 
					
						
							|  |  |  |                             s += ",\"" + k + "\":" + this._encode(v, depth + 1); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     } catch (e) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                         foreach(k, v in val.getclass()) { | 
					
						
							|  |  |  |                             if (typeof v != "function") { | 
					
						
							|  |  |  |                                 s += ",\"" + k + "\":" + this._encode(val[k], depth + 1); | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     s = s.len() > 0 ? s.slice(1) : s; | 
					
						
							|  |  |  |                     r += "{" + s + "}"; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             case "blob": | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                 r += "\"" + (val.len() ? this._escape(val.tostring()) : "") + "\""; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |             default: | 
					
						
							|  |  |  |                 r += "\"" + this._escape(val.tostring()) + "\""; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return r; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function _escape(str) { | 
					
						
							|  |  |  |         local res = ""; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (local i = 0; i< str.len(); i++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             local ch1 = (str[i] & 0xFF); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if ((ch1 & 0x80) == 0x00) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 ch1 = format("%c", ch1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (ch1 == "\"") { | 
					
						
							|  |  |  |                     res += "\\\""; | 
					
						
							|  |  |  |                 } else if (ch1 == "\\") { | 
					
						
							|  |  |  |                     res += "\\\\"; | 
					
						
							|  |  |  |                 } else if (ch1 == "/") { | 
					
						
							|  |  |  |                     res += "\\/"; | 
					
						
							|  |  |  |                 } else if (ch1 == "\b") { | 
					
						
							|  |  |  |                     res += "\\b"; | 
					
						
							|  |  |  |                 } else if (ch1 == "\f") { | 
					
						
							|  |  |  |                     res += "\\f"; | 
					
						
							|  |  |  |                 } else if (ch1 == "\n") { | 
					
						
							|  |  |  |                     res += "\\n"; | 
					
						
							|  |  |  |                 } else if (ch1 == "\r") { | 
					
						
							|  |  |  |                     res += "\\r"; | 
					
						
							|  |  |  |                 } else if (ch1 == "\t") { | 
					
						
							|  |  |  |                     res += "\\t"; | 
					
						
							|  |  |  |                 } else if (ch1 == "\0") { | 
					
						
							|  |  |  |                     res += "\\u0000"; | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     res += ch1; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if ((ch1 & 0xE0) == 0xC0) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                     local ch2 = (str[++i] & 0xFF); | 
					
						
							|  |  |  |                     res += format("%c%c", ch1, ch2); | 
					
						
							|  |  |  |                 } else if ((ch1 & 0xF0) == 0xE0) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                     local ch2 = (str[++i] & 0xFF); | 
					
						
							|  |  |  |                     local ch3 = (str[++i] & 0xFF); | 
					
						
							|  |  |  |                     res += format("%c%c%c", ch1, ch2, ch3); | 
					
						
							|  |  |  |                 } else if ((ch1 & 0xF8) == 0xF0) { | 
					
						
							| 
									
										
										
										
											2025-05-27 21:24:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-29 16:52:10 +08:00
										 |  |  |                     local ch2 = (str[++i] & 0xFF); | 
					
						
							|  |  |  |                     local ch3 = (str[++i] & 0xFF); | 
					
						
							|  |  |  |                     local ch4 = (str[++i] & 0xFF); | 
					
						
							|  |  |  |                     res += format("%c%c%c%c", ch1, ch2, ch3, ch4); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |