UNPKG

13.1 kBJavaScriptView Raw
1/*---------------------------------------------------------
2 * Copyright (C) Microsoft Corporation. All rights reserved.
3 *--------------------------------------------------------*/
4'use strict';
5function parseWithLocation(content, filename, locationKeyName) {
6 return _parse(content, filename, locationKeyName);
7}
8exports.parseWithLocation = parseWithLocation;
9/**
10 * A very fast plist parser
11 */
12function parse(content) {
13 return _parse(content, null, null);
14}
15exports.parse = parse;
16function _parse(content, filename, locationKeyName) {
17 var len = content.length;
18 var pos = 0;
19 var line = 1;
20 var char = 0;
21 // Skip UTF8 BOM
22 if (len > 0 && content.charCodeAt(0) === 65279 /* BOM */) {
23 pos = 1;
24 }
25 function advancePosBy(by) {
26 if (locationKeyName === null) {
27 pos = pos + by;
28 }
29 else {
30 while (by > 0) {
31 var chCode = content.charCodeAt(pos);
32 if (chCode === 10 /* LINE_FEED */) {
33 pos++;
34 line++;
35 char = 0;
36 }
37 else {
38 pos++;
39 char++;
40 }
41 by--;
42 }
43 }
44 }
45 function advancePosTo(to) {
46 if (locationKeyName === null) {
47 pos = to;
48 }
49 else {
50 advancePosBy(to - pos);
51 }
52 }
53 function skipWhitespace() {
54 while (pos < len) {
55 var chCode = content.charCodeAt(pos);
56 if (chCode !== 32 /* SPACE */ && chCode !== 9 /* TAB */ && chCode !== 13 /* CARRIAGE_RETURN */ && chCode !== 10 /* LINE_FEED */) {
57 break;
58 }
59 advancePosBy(1);
60 }
61 }
62 function advanceIfStartsWith(str) {
63 if (content.substr(pos, str.length) === str) {
64 advancePosBy(str.length);
65 return true;
66 }
67 return false;
68 }
69 function advanceUntil(str) {
70 var nextOccurence = content.indexOf(str, pos);
71 if (nextOccurence !== -1) {
72 advancePosTo(nextOccurence + str.length);
73 }
74 else {
75 // EOF
76 advancePosTo(len);
77 }
78 }
79 function captureUntil(str) {
80 var nextOccurence = content.indexOf(str, pos);
81 if (nextOccurence !== -1) {
82 var r = content.substring(pos, nextOccurence);
83 advancePosTo(nextOccurence + str.length);
84 return r;
85 }
86 else {
87 // EOF
88 var r = content.substr(pos);
89 advancePosTo(len);
90 return r;
91 }
92 }
93 var state = 0 /* ROOT_STATE */;
94 var cur = null;
95 var stateStack = [];
96 var objStack = [];
97 var curKey = null;
98 function pushState(newState, newCur) {
99 stateStack.push(state);
100 objStack.push(cur);
101 state = newState;
102 cur = newCur;
103 }
104 function popState() {
105 state = stateStack.pop();
106 cur = objStack.pop();
107 }
108 function fail(msg) {
109 throw new Error('Near offset ' + pos + ': ' + msg + ' ~~~' + content.substr(pos, 50) + '~~~');
110 }
111 var dictState = {
112 enterDict: function () {
113 if (curKey === null) {
114 fail('missing <key>');
115 }
116 var newDict = {};
117 if (locationKeyName !== null) {
118 newDict[locationKeyName] = {
119 filename: filename,
120 line: line,
121 char: char
122 };
123 }
124 cur[curKey] = newDict;
125 curKey = null;
126 pushState(1 /* DICT_STATE */, newDict);
127 },
128 enterArray: function () {
129 if (curKey === null) {
130 fail('missing <key>');
131 }
132 var newArr = [];
133 cur[curKey] = newArr;
134 curKey = null;
135 pushState(2 /* ARR_STATE */, newArr);
136 }
137 };
138 var arrState = {
139 enterDict: function () {
140 var newDict = {};
141 if (locationKeyName !== null) {
142 newDict[locationKeyName] = {
143 filename: filename,
144 line: line,
145 char: char
146 };
147 }
148 cur.push(newDict);
149 pushState(1 /* DICT_STATE */, newDict);
150 },
151 enterArray: function () {
152 var newArr = [];
153 cur.push(newArr);
154 pushState(2 /* ARR_STATE */, newArr);
155 }
156 };
157 function enterDict() {
158 if (state === 1 /* DICT_STATE */) {
159 dictState.enterDict();
160 }
161 else if (state === 2 /* ARR_STATE */) {
162 arrState.enterDict();
163 }
164 else {
165 cur = {};
166 if (locationKeyName !== null) {
167 cur[locationKeyName] = {
168 filename: filename,
169 line: line,
170 char: char
171 };
172 }
173 pushState(1 /* DICT_STATE */, cur);
174 }
175 }
176 function leaveDict() {
177 if (state === 1 /* DICT_STATE */) {
178 popState();
179 }
180 else if (state === 2 /* ARR_STATE */) {
181 fail('unexpected </dict>');
182 }
183 else {
184 fail('unexpected </dict>');
185 }
186 }
187 function enterArray() {
188 if (state === 1 /* DICT_STATE */) {
189 dictState.enterArray();
190 }
191 else if (state === 2 /* ARR_STATE */) {
192 arrState.enterArray();
193 }
194 else {
195 cur = [];
196 pushState(2 /* ARR_STATE */, cur);
197 }
198 }
199 function leaveArray() {
200 if (state === 1 /* DICT_STATE */) {
201 fail('unexpected </array>');
202 }
203 else if (state === 2 /* ARR_STATE */) {
204 popState();
205 }
206 else {
207 fail('unexpected </array>');
208 }
209 }
210 function acceptKey(val) {
211 if (state === 1 /* DICT_STATE */) {
212 if (curKey !== null) {
213 fail('too many <key>');
214 }
215 curKey = val;
216 }
217 else if (state === 2 /* ARR_STATE */) {
218 fail('unexpected <key>');
219 }
220 else {
221 fail('unexpected <key>');
222 }
223 }
224 function acceptString(val) {
225 if (state === 1 /* DICT_STATE */) {
226 if (curKey === null) {
227 fail('missing <key>');
228 }
229 cur[curKey] = val;
230 curKey = null;
231 }
232 else if (state === 2 /* ARR_STATE */) {
233 cur.push(val);
234 }
235 else {
236 cur = val;
237 }
238 }
239 function acceptReal(val) {
240 if (isNaN(val)) {
241 fail('cannot parse float');
242 }
243 if (state === 1 /* DICT_STATE */) {
244 if (curKey === null) {
245 fail('missing <key>');
246 }
247 cur[curKey] = val;
248 curKey = null;
249 }
250 else if (state === 2 /* ARR_STATE */) {
251 cur.push(val);
252 }
253 else {
254 cur = val;
255 }
256 }
257 function acceptInteger(val) {
258 if (isNaN(val)) {
259 fail('cannot parse integer');
260 }
261 if (state === 1 /* DICT_STATE */) {
262 if (curKey === null) {
263 fail('missing <key>');
264 }
265 cur[curKey] = val;
266 curKey = null;
267 }
268 else if (state === 2 /* ARR_STATE */) {
269 cur.push(val);
270 }
271 else {
272 cur = val;
273 }
274 }
275 function acceptDate(val) {
276 if (state === 1 /* DICT_STATE */) {
277 if (curKey === null) {
278 fail('missing <key>');
279 }
280 cur[curKey] = val;
281 curKey = null;
282 }
283 else if (state === 2 /* ARR_STATE */) {
284 cur.push(val);
285 }
286 else {
287 cur = val;
288 }
289 }
290 function acceptData(val) {
291 if (state === 1 /* DICT_STATE */) {
292 if (curKey === null) {
293 fail('missing <key>');
294 }
295 cur[curKey] = val;
296 curKey = null;
297 }
298 else if (state === 2 /* ARR_STATE */) {
299 cur.push(val);
300 }
301 else {
302 cur = val;
303 }
304 }
305 function acceptBool(val) {
306 if (state === 1 /* DICT_STATE */) {
307 if (curKey === null) {
308 fail('missing <key>');
309 }
310 cur[curKey] = val;
311 curKey = null;
312 }
313 else if (state === 2 /* ARR_STATE */) {
314 cur.push(val);
315 }
316 else {
317 cur = val;
318 }
319 }
320 function escapeVal(str) {
321 return str.replace(/&#([0-9]+);/g, function (_, m0) {
322 return String.fromCodePoint(parseInt(m0, 10));
323 }).replace(/&#x([0-9a-f]+);/g, function (_, m0) {
324 return String.fromCodePoint(parseInt(m0, 16));
325 }).replace(/&amp;|&lt;|&gt;|&quot;|&apos;/g, function (_) {
326 switch (_) {
327 case '&amp;': return '&';
328 case '&lt;': return '<';
329 case '&gt;': return '>';
330 case '&quot;': return '"';
331 case '&apos;': return '\'';
332 }
333 return _;
334 });
335 }
336 function parseOpenTag() {
337 var r = captureUntil('>');
338 var isClosed = false;
339 if (r.charCodeAt(r.length - 1) === 47 /* SLASH */) {
340 isClosed = true;
341 r = r.substring(0, r.length - 1);
342 }
343 return {
344 name: r.trim(),
345 isClosed: isClosed
346 };
347 }
348 function parseTagValue(tag) {
349 if (tag.isClosed) {
350 return '';
351 }
352 var val = captureUntil('</');
353 advanceUntil('>');
354 return escapeVal(val);
355 }
356 while (pos < len) {
357 skipWhitespace();
358 if (pos >= len) {
359 break;
360 }
361 var chCode = content.charCodeAt(pos);
362 advancePosBy(1);
363 if (chCode !== 60 /* LESS_THAN */) {
364 fail('expected <');
365 }
366 if (pos >= len) {
367 fail('unexpected end of input');
368 }
369 var peekChCode = content.charCodeAt(pos);
370 if (peekChCode === 63 /* QUESTION_MARK */) {
371 advancePosBy(1);
372 advanceUntil('?>');
373 continue;
374 }
375 if (peekChCode === 33 /* EXCLAMATION_MARK */) {
376 advancePosBy(1);
377 if (advanceIfStartsWith('--')) {
378 advanceUntil('-->');
379 continue;
380 }
381 advanceUntil('>');
382 continue;
383 }
384 if (peekChCode === 47 /* SLASH */) {
385 advancePosBy(1);
386 skipWhitespace();
387 if (advanceIfStartsWith('plist')) {
388 advanceUntil('>');
389 continue;
390 }
391 if (advanceIfStartsWith('dict')) {
392 advanceUntil('>');
393 leaveDict();
394 continue;
395 }
396 if (advanceIfStartsWith('array')) {
397 advanceUntil('>');
398 leaveArray();
399 continue;
400 }
401 fail('unexpected closed tag');
402 }
403 var tag = parseOpenTag();
404 switch (tag.name) {
405 case 'dict':
406 enterDict();
407 if (tag.isClosed) {
408 leaveDict();
409 }
410 continue;
411 case 'array':
412 enterArray();
413 if (tag.isClosed) {
414 leaveArray();
415 }
416 continue;
417 case 'key':
418 acceptKey(parseTagValue(tag));
419 continue;
420 case 'string':
421 acceptString(parseTagValue(tag));
422 continue;
423 case 'real':
424 acceptReal(parseFloat(parseTagValue(tag)));
425 continue;
426 case 'integer':
427 acceptInteger(parseInt(parseTagValue(tag), 10));
428 continue;
429 case 'date':
430 acceptDate(new Date(parseTagValue(tag)));
431 continue;
432 case 'data':
433 acceptData(parseTagValue(tag));
434 continue;
435 case 'true':
436 parseTagValue(tag);
437 acceptBool(true);
438 continue;
439 case 'false':
440 parseTagValue(tag);
441 acceptBool(false);
442 continue;
443 }
444 if (/^plist/.test(tag.name)) {
445 continue;
446 }
447 fail('unexpected opened tag ' + tag.name);
448 }
449 return cur;
450}
451//# sourceMappingURL=main.js.map
\No newline at end of file