parse.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. 'use strict';
  2. var at; // The index of the current character
  3. var ch; // The current character
  4. var escapee = {
  5. '"': '"',
  6. '\\': '\\',
  7. '/': '/',
  8. b: '\b',
  9. f: '\f',
  10. n: '\n',
  11. r: '\r',
  12. t: '\t'
  13. };
  14. var text;
  15. // Call error when something is wrong.
  16. function error(m) {
  17. throw {
  18. name: 'SyntaxError',
  19. message: m,
  20. at: at,
  21. text: text
  22. };
  23. }
  24. function next(c) {
  25. // If a c parameter is provided, verify that it matches the current character.
  26. if (c && c !== ch) {
  27. error("Expected '" + c + "' instead of '" + ch + "'");
  28. }
  29. // Get the next character. When there are no more characters, return the empty string.
  30. ch = text.charAt(at);
  31. at += 1;
  32. return ch;
  33. }
  34. function number() {
  35. // Parse a number value.
  36. var num;
  37. var str = '';
  38. if (ch === '-') {
  39. str = '-';
  40. next('-');
  41. }
  42. while (ch >= '0' && ch <= '9') {
  43. str += ch;
  44. next();
  45. }
  46. if (ch === '.') {
  47. str += '.';
  48. while (next() && ch >= '0' && ch <= '9') {
  49. str += ch;
  50. }
  51. }
  52. if (ch === 'e' || ch === 'E') {
  53. str += ch;
  54. next();
  55. if (ch === '-' || ch === '+') {
  56. str += ch;
  57. next();
  58. }
  59. while (ch >= '0' && ch <= '9') {
  60. str += ch;
  61. next();
  62. }
  63. }
  64. num = Number(str);
  65. if (!isFinite(num)) {
  66. error('Bad number');
  67. }
  68. return num;
  69. }
  70. function string() {
  71. // Parse a string value.
  72. var hex;
  73. var i;
  74. var str = '';
  75. var uffff;
  76. // When parsing for string values, we must look for " and \ characters.
  77. if (ch === '"') {
  78. while (next()) {
  79. if (ch === '"') {
  80. next();
  81. return str;
  82. } else if (ch === '\\') {
  83. next();
  84. if (ch === 'u') {
  85. uffff = 0;
  86. for (i = 0; i < 4; i += 1) {
  87. hex = parseInt(next(), 16);
  88. if (!isFinite(hex)) {
  89. break;
  90. }
  91. uffff = (uffff * 16) + hex;
  92. }
  93. str += String.fromCharCode(uffff);
  94. } else if (typeof escapee[ch] === 'string') {
  95. str += escapee[ch];
  96. } else {
  97. break;
  98. }
  99. } else {
  100. str += ch;
  101. }
  102. }
  103. }
  104. error('Bad string');
  105. }
  106. // Skip whitespace.
  107. function white() {
  108. while (ch && ch <= ' ') {
  109. next();
  110. }
  111. }
  112. // true, false, or null.
  113. function word() {
  114. switch (ch) {
  115. case 't':
  116. next('t');
  117. next('r');
  118. next('u');
  119. next('e');
  120. return true;
  121. case 'f':
  122. next('f');
  123. next('a');
  124. next('l');
  125. next('s');
  126. next('e');
  127. return false;
  128. case 'n':
  129. next('n');
  130. next('u');
  131. next('l');
  132. next('l');
  133. return null;
  134. default:
  135. error("Unexpected '" + ch + "'");
  136. }
  137. }
  138. // Parse an array value.
  139. function array() {
  140. var arr = [];
  141. if (ch === '[') {
  142. next('[');
  143. white();
  144. if (ch === ']') {
  145. next(']');
  146. return arr; // empty array
  147. }
  148. while (ch) {
  149. arr.push(value()); // eslint-disable-line no-use-before-define
  150. white();
  151. if (ch === ']') {
  152. next(']');
  153. return arr;
  154. }
  155. next(',');
  156. white();
  157. }
  158. }
  159. error('Bad array');
  160. }
  161. // Parse an object value.
  162. function object() {
  163. var key;
  164. var obj = {};
  165. if (ch === '{') {
  166. next('{');
  167. white();
  168. if (ch === '}') {
  169. next('}');
  170. return obj; // empty object
  171. }
  172. while (ch) {
  173. key = string();
  174. white();
  175. next(':');
  176. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  177. error('Duplicate key "' + key + '"');
  178. }
  179. obj[key] = value(); // eslint-disable-line no-use-before-define
  180. white();
  181. if (ch === '}') {
  182. next('}');
  183. return obj;
  184. }
  185. next(',');
  186. white();
  187. }
  188. }
  189. error('Bad object');
  190. }
  191. // Parse a JSON value. It could be an object, an array, a string, a number, or a word.
  192. function value() {
  193. white();
  194. switch (ch) {
  195. case '{':
  196. return object();
  197. case '[':
  198. return array();
  199. case '"':
  200. return string();
  201. case '-':
  202. return number();
  203. default:
  204. return ch >= '0' && ch <= '9' ? number() : word();
  205. }
  206. }
  207. // Return the json_parse function. It will have access to all of the above functions and variables.
  208. module.exports = function (source, reviver) {
  209. var result;
  210. text = source;
  211. at = 0;
  212. ch = ' ';
  213. result = value();
  214. white();
  215. if (ch) {
  216. error('Syntax error');
  217. }
  218. // If there is a reviver function, we recursively walk the new structure,
  219. // passing each name/value pair to the reviver function for possible
  220. // transformation, starting with a temporary root object that holds the result
  221. // in an empty key. If there is not a reviver function, we simply return the
  222. // result.
  223. return typeof reviver === 'function' ? (function walk(holder, key) {
  224. var k;
  225. var v;
  226. var val = holder[key];
  227. if (val && typeof val === 'object') {
  228. for (k in value) {
  229. if (Object.prototype.hasOwnProperty.call(val, k)) {
  230. v = walk(val, k);
  231. if (typeof v === 'undefined') {
  232. delete val[k];
  233. } else {
  234. val[k] = v;
  235. }
  236. }
  237. }
  238. }
  239. return reviver.call(holder, key, val);
  240. }({ '': result }, '')) : result;
  241. };