源战役客户端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

535 lines
12 KiB

  1. /******************************************************************************
  2. * Spine Runtimes Software License v2.5
  3. *
  4. * Copyright (c) 2013-2016, Esoteric Software
  5. * All rights reserved.
  6. *
  7. * You are granted a perpetual, non-exclusive, non-sublicensable, and
  8. * non-transferable license to use, install, execute, and perform the Spine
  9. * Runtimes software and derivative works solely for personal or internal
  10. * use. Without the written permission of Esoteric Software (see Section 2 of
  11. * the Spine Software License Agreement), you may not (a) modify, translate,
  12. * adapt, or develop new applications using the Spine Runtimes or otherwise
  13. * create derivative works or improvements of the Spine Runtimes or (b) remove,
  14. * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
  15. * or other intellectual property or proprietary rights notices on or in the
  16. * Software, including any copy thereof. Redistributions in binary or source
  17. * form must include this license and terms.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
  20. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  22. * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  24. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
  25. * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  26. * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. * POSSIBILITY OF SUCH DAMAGE.
  29. *****************************************************************************/
  30. using System;
  31. using System.IO;
  32. using System.Text;
  33. using System.Collections;
  34. using System.Globalization;
  35. using System.Collections.Generic;
  36. namespace Spine {
  37. public static class Json {
  38. public static object Deserialize (TextReader text) {
  39. var parser = new SharpJson.JsonDecoder();
  40. parser.parseNumbersAsFloat = true;
  41. return parser.Decode(text.ReadToEnd());
  42. }
  43. }
  44. }
  45. /**
  46. *
  47. * Copyright (c) 2016 Adriano Tinoco d'Oliveira Rezende
  48. *
  49. * Based on the JSON parser by Patrick van Bergen
  50. * http://techblog.procurios.nl/k/news/view/14605/14863/how-do-i-write-my-own-parser-(for-json).html
  51. *
  52. * Changes made:
  53. *
  54. * - Optimized parser speed (deserialize roughly near 3x faster than original)
  55. * - Added support to handle lexer/parser error messages with line numbers
  56. * - Added more fine grained control over type conversions during the parsing
  57. * - Refactory API (Separate Lexer code from Parser code and the Encoder from Decoder)
  58. *
  59. *
  60. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
  61. * and associated documentation files (the "Software"), to deal in the Software without restriction,
  62. * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
  63. * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
  64. * subject to the following conditions:
  65. * The above copyright notice and this permission notice shall be included in all copies or substantial
  66. * portions of the Software.
  67. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
  68. * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  69. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  70. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  71. * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  72. *
  73. */
  74. namespace SharpJson
  75. {
  76. class Lexer
  77. {
  78. public enum Token {
  79. None,
  80. Null,
  81. True,
  82. False,
  83. Colon,
  84. Comma,
  85. String,
  86. Number,
  87. CurlyOpen,
  88. CurlyClose,
  89. SquaredOpen,
  90. SquaredClose,
  91. };
  92. public bool hasError {
  93. get {
  94. return !success;
  95. }
  96. }
  97. public int lineNumber {
  98. get;
  99. private set;
  100. }
  101. public bool parseNumbersAsFloat {
  102. get;
  103. set;
  104. }
  105. char[] json;
  106. int index = 0;
  107. bool success = true;
  108. char[] stringBuffer = new char[4096];
  109. public Lexer(string text)
  110. {
  111. Reset();
  112. json = text.ToCharArray();
  113. parseNumbersAsFloat = false;
  114. }
  115. public void Reset()
  116. {
  117. index = 0;
  118. lineNumber = 1;
  119. success = true;
  120. }
  121. public string ParseString()
  122. {
  123. int idx = 0;
  124. StringBuilder builder = null;
  125. SkipWhiteSpaces();
  126. // "
  127. char c = json[index++];
  128. bool failed = false;
  129. bool complete = false;
  130. while (!complete && !failed) {
  131. if (index == json.Length)
  132. break;
  133. c = json[index++];
  134. if (c == '"') {
  135. complete = true;
  136. break;
  137. } else if (c == '\\') {
  138. if (index == json.Length)
  139. break;
  140. c = json[index++];
  141. switch (c) {
  142. case '"':
  143. stringBuffer[idx++] = '"';
  144. break;
  145. case '\\':
  146. stringBuffer[idx++] = '\\';
  147. break;
  148. case '/':
  149. stringBuffer[idx++] = '/';
  150. break;
  151. case 'b':
  152. stringBuffer[idx++] = '\b';
  153. break;
  154. case'f':
  155. stringBuffer[idx++] = '\f';
  156. break;
  157. case 'n':
  158. stringBuffer[idx++] = '\n';
  159. break;
  160. case 'r':
  161. stringBuffer[idx++] = '\r';
  162. break;
  163. case 't':
  164. stringBuffer[idx++] = '\t';
  165. break;
  166. case 'u':
  167. int remainingLength = json.Length - index;
  168. if (remainingLength >= 4) {
  169. var hex = new string(json, index, 4);
  170. // XXX: handle UTF
  171. stringBuffer[idx++] = (char) Convert.ToInt32(hex, 16);
  172. // skip 4 chars
  173. index += 4;
  174. } else {
  175. failed = true;
  176. }
  177. break;
  178. }
  179. } else {
  180. stringBuffer[idx++] = c;
  181. }
  182. if (idx >= stringBuffer.Length) {
  183. if (builder == null)
  184. builder = new StringBuilder();
  185. builder.Append(stringBuffer, 0, idx);
  186. idx = 0;
  187. }
  188. }
  189. if (!complete) {
  190. success = false;
  191. return null;
  192. }
  193. if (builder != null)
  194. return builder.ToString ();
  195. else
  196. return new string (stringBuffer, 0, idx);
  197. }
  198. string GetNumberString()
  199. {
  200. SkipWhiteSpaces();
  201. int lastIndex = GetLastIndexOfNumber(index);
  202. int charLength = (lastIndex - index) + 1;
  203. var result = new string (json, index, charLength);
  204. index = lastIndex + 1;
  205. return result;
  206. }
  207. public float ParseFloatNumber()
  208. {
  209. float number;
  210. var str = GetNumberString ();
  211. if (!float.TryParse (str, NumberStyles.Float, CultureInfo.InvariantCulture, out number))
  212. return 0;
  213. return number;
  214. }
  215. public double ParseDoubleNumber()
  216. {
  217. double number;
  218. var str = GetNumberString ();
  219. if (!double.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
  220. return 0;
  221. return number;
  222. }
  223. int GetLastIndexOfNumber(int index)
  224. {
  225. int lastIndex;
  226. for (lastIndex = index; lastIndex < json.Length; lastIndex++) {
  227. char ch = json[lastIndex];
  228. if ((ch < '0' || ch > '9') && ch != '+' && ch != '-'
  229. && ch != '.' && ch != 'e' && ch != 'E')
  230. break;
  231. }
  232. return lastIndex - 1;
  233. }
  234. void SkipWhiteSpaces()
  235. {
  236. for (; index < json.Length; index++) {
  237. char ch = json[index];
  238. if (ch == '\n')
  239. lineNumber++;
  240. if (!char.IsWhiteSpace(json[index]))
  241. break;
  242. }
  243. }
  244. public Token LookAhead()
  245. {
  246. SkipWhiteSpaces();
  247. int savedIndex = index;
  248. return NextToken(json, ref savedIndex);
  249. }
  250. public Token NextToken()
  251. {
  252. SkipWhiteSpaces();
  253. return NextToken(json, ref index);
  254. }
  255. static Token NextToken(char[] json, ref int index)
  256. {
  257. if (index == json.Length)
  258. return Token.None;
  259. char c = json[index++];
  260. switch (c) {
  261. case '{':
  262. return Token.CurlyOpen;
  263. case '}':
  264. return Token.CurlyClose;
  265. case '[':
  266. return Token.SquaredOpen;
  267. case ']':
  268. return Token.SquaredClose;
  269. case ',':
  270. return Token.Comma;
  271. case '"':
  272. return Token.String;
  273. case '0': case '1': case '2': case '3': case '4':
  274. case '5': case '6': case '7': case '8': case '9':
  275. case '-':
  276. return Token.Number;
  277. case ':':
  278. return Token.Colon;
  279. }
  280. index--;
  281. int remainingLength = json.Length - index;
  282. // false
  283. if (remainingLength >= 5) {
  284. if (json[index] == 'f' &&
  285. json[index + 1] == 'a' &&
  286. json[index + 2] == 'l' &&
  287. json[index + 3] == 's' &&
  288. json[index + 4] == 'e') {
  289. index += 5;
  290. return Token.False;
  291. }
  292. }
  293. // true
  294. if (remainingLength >= 4) {
  295. if (json[index] == 't' &&
  296. json[index + 1] == 'r' &&
  297. json[index + 2] == 'u' &&
  298. json[index + 3] == 'e') {
  299. index += 4;
  300. return Token.True;
  301. }
  302. }
  303. // null
  304. if (remainingLength >= 4) {
  305. if (json[index] == 'n' &&
  306. json[index + 1] == 'u' &&
  307. json[index + 2] == 'l' &&
  308. json[index + 3] == 'l') {
  309. index += 4;
  310. return Token.Null;
  311. }
  312. }
  313. return Token.None;
  314. }
  315. }
  316. public class JsonDecoder
  317. {
  318. public string errorMessage {
  319. get;
  320. private set;
  321. }
  322. public bool parseNumbersAsFloat {
  323. get;
  324. set;
  325. }
  326. Lexer lexer;
  327. public JsonDecoder()
  328. {
  329. errorMessage = null;
  330. parseNumbersAsFloat = false;
  331. }
  332. public object Decode(string text)
  333. {
  334. errorMessage = null;
  335. lexer = new Lexer(text);
  336. lexer.parseNumbersAsFloat = parseNumbersAsFloat;
  337. return ParseValue();
  338. }
  339. public static object DecodeText(string text)
  340. {
  341. var builder = new JsonDecoder();
  342. return builder.Decode(text);
  343. }
  344. IDictionary<string, object> ParseObject()
  345. {
  346. var table = new Dictionary<string, object>();
  347. // {
  348. lexer.NextToken();
  349. while (true) {
  350. var token = lexer.LookAhead();
  351. switch (token) {
  352. case Lexer.Token.None:
  353. TriggerError("Invalid token");
  354. return null;
  355. case Lexer.Token.Comma:
  356. lexer.NextToken();
  357. break;
  358. case Lexer.Token.CurlyClose:
  359. lexer.NextToken();
  360. return table;
  361. default:
  362. // name
  363. string name = EvalLexer(lexer.ParseString());
  364. if (errorMessage != null)
  365. return null;
  366. // :
  367. token = lexer.NextToken();
  368. if (token != Lexer.Token.Colon) {
  369. TriggerError("Invalid token; expected ':'");
  370. return null;
  371. }
  372. // value
  373. object value = ParseValue();
  374. if (errorMessage != null)
  375. return null;
  376. table[name] = value;
  377. break;
  378. }
  379. }
  380. //return null; // Unreachable code
  381. }
  382. IList<object> ParseArray()
  383. {
  384. var array = new List<object>();
  385. // [
  386. lexer.NextToken();
  387. while (true) {
  388. var token = lexer.LookAhead();
  389. switch (token) {
  390. case Lexer.Token.None:
  391. TriggerError("Invalid token");
  392. return null;
  393. case Lexer.Token.Comma:
  394. lexer.NextToken();
  395. break;
  396. case Lexer.Token.SquaredClose:
  397. lexer.NextToken();
  398. return array;
  399. default:
  400. object value = ParseValue();
  401. if (errorMessage != null)
  402. return null;
  403. array.Add(value);
  404. break;
  405. }
  406. }
  407. //return null; // Unreachable code
  408. }
  409. object ParseValue()
  410. {
  411. switch (lexer.LookAhead()) {
  412. case Lexer.Token.String:
  413. return EvalLexer(lexer.ParseString());
  414. case Lexer.Token.Number:
  415. if (parseNumbersAsFloat)
  416. return EvalLexer(lexer.ParseFloatNumber());
  417. else
  418. return EvalLexer(lexer.ParseDoubleNumber());
  419. case Lexer.Token.CurlyOpen:
  420. return ParseObject();
  421. case Lexer.Token.SquaredOpen:
  422. return ParseArray();
  423. case Lexer.Token.True:
  424. lexer.NextToken();
  425. return true;
  426. case Lexer.Token.False:
  427. lexer.NextToken();
  428. return false;
  429. case Lexer.Token.Null:
  430. lexer.NextToken();
  431. return null;
  432. case Lexer.Token.None:
  433. break;
  434. }
  435. TriggerError("Unable to parse value");
  436. return null;
  437. }
  438. void TriggerError(string message)
  439. {
  440. errorMessage = string.Format("Error: '{0}' at line {1}",
  441. message, lexer.lineNumber);
  442. }
  443. T EvalLexer<T>(T value)
  444. {
  445. if (lexer.hasError)
  446. TriggerError("Lexical error ocurred");
  447. return value;
  448. }
  449. }
  450. }