@@ -29,15 +29,7 @@ var unescapeMap = {
29
29
'/' : '/'
30
30
}
31
31
32
- function parseCustom ( input , options ) { // eslint-disable-line no-unused-vars
33
- if ( typeof options === 'function' ) {
34
- options = {
35
- reviver : options
36
- }
37
- } else if ( ! options ) {
38
- options = { }
39
- }
40
-
32
+ function parseInternal ( input , options ) {
41
33
if ( typeof input !== 'string' || ! ( input instanceof String ) ) {
42
34
input = String ( input )
43
35
}
@@ -48,6 +40,10 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
48
40
var allowSingleQuotedStrings = options . allowSingleQuotedStrings || json5
49
41
var allowDuplicateObjectKeys = options . allowDuplicateObjectKeys
50
42
var reviver = options . reviver
43
+ var tokenize = options . tokenize
44
+ var rawTokens = options . rawTokens
45
+ var tokenLocations = options . tokenLocations
46
+ var tokenPaths = options . tokenPaths
51
47
52
48
var isLineTerminator = json5 ? Uni . isLineTerminator : Uni . isLineTerminatorJSON
53
49
var isWhiteSpace = json5 ? Uni . isWhiteSpace : Uni . isWhiteSpaceJSON
@@ -56,7 +52,50 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
56
52
var lineNumber = 0
57
53
var lineStart = 0
58
54
var position = 0
59
- var stack = [ ]
55
+
56
+ var startToken
57
+ var endToken
58
+ var tokenPath
59
+
60
+ if ( tokenize ) {
61
+ var tokens = [ ]
62
+ var tokenOffset = null
63
+ var tokenLine
64
+ var tokenColumn
65
+ startToken = function ( ) {
66
+ if ( tokenOffset !== null ) throw Error ( 'internal error, token overlap' )
67
+ tokenLine = lineNumber + 1
68
+ tokenColumn = position - lineStart + 1
69
+ tokenOffset = position
70
+ }
71
+ endToken = function ( type , value ) {
72
+ if ( tokenOffset !== position ) {
73
+ var token = { type : type }
74
+ if ( rawTokens ) {
75
+ token . raw = input . substr ( tokenOffset , position - tokenOffset )
76
+ }
77
+ if ( value !== undefined ) {
78
+ token . value = value
79
+ }
80
+ if ( tokenLocations ) {
81
+ token . location = {
82
+ start : {
83
+ column : tokenColumn ,
84
+ line : tokenLine ,
85
+ offset : tokenOffset
86
+ }
87
+ }
88
+ }
89
+ if ( tokenPaths ) {
90
+ token . path = tokenPath . slice ( )
91
+ }
92
+ tokens . push ( token )
93
+ }
94
+ tokenOffset = null
95
+ return value
96
+ }
97
+ tokenPaths && ( tokenPath = [ ] )
98
+ }
60
99
61
100
function generateMessage ( ) {
62
101
var message
@@ -106,27 +145,38 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
106
145
107
146
function parseGeneric ( ) {
108
147
while ( position < inputLength ) {
148
+ startToken && startToken ( )
109
149
var char = input [ position ++ ]
110
150
if ( char === '"' || ( char === '\'' && allowSingleQuotedStrings ) ) {
111
- return parseString ( char )
151
+ var string = parseString ( char )
152
+ endToken && endToken ( 'literal' , string )
153
+ return string
112
154
} else if ( char === '{' ) {
155
+ endToken && endToken ( 'symbol' , '{' )
113
156
return parseObject ( )
114
157
} else if ( char === '[' ) {
158
+ endToken && endToken ( 'symbol' , '[' )
115
159
return parseArray ( )
116
160
} else if ( char === '-' || char === '.' || isDecDigit ( char ) ||
117
161
( json5 && ( char === '+' || char === 'I' || char === 'N' ) ) ) {
118
- return parseNumber ( )
162
+ var number = parseNumber ( )
163
+ endToken && endToken ( 'literal' , number )
164
+ return number
119
165
} else if ( char === 'n' ) {
120
166
parseKeyword ( 'null' )
167
+ endToken && endToken ( 'literal' , null )
121
168
return null
122
169
} else if ( char === 't' ) {
123
170
parseKeyword ( 'true' )
171
+ endToken && endToken ( 'literal' , true )
124
172
return true
125
173
} else if ( char === 'f' ) {
126
174
parseKeyword ( 'false' )
175
+ endToken && endToken ( 'literal' , false )
127
176
return false
128
177
} else {
129
178
-- position
179
+ endToken && endToken ( )
130
180
return undefined
131
181
}
132
182
}
@@ -135,47 +185,81 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
135
185
function parseKey ( ) {
136
186
var result
137
187
while ( position < inputLength ) {
188
+ startToken && startToken ( )
138
189
var char = input [ position ++ ]
139
190
if ( char === '"' || ( char === '\'' && allowSingleQuotedStrings ) ) {
140
- return parseString ( char )
191
+ var string = parseString ( char )
192
+ endToken && endToken ( 'literal' , string )
193
+ return string
141
194
} else if ( char === '{' ) {
195
+ endToken && endToken ( 'symbol' , '{' )
142
196
return parseObject ( )
143
197
} else if ( char === '[' ) {
198
+ endToken && endToken ( 'symbol' , '[' )
144
199
return parseArray ( )
145
200
} else if ( char === '.' || isDecDigit ( char ) ) {
146
- return parseNumber ( true )
201
+ var number = parseNumber ( true )
202
+ endToken && endToken ( 'literal' , number )
203
+ return number
147
204
} else if ( ( json5 && Uni . isIdentifierStart ( char ) ) ||
148
205
( char === '\\' && input [ position ] === 'u' ) ) {
149
206
var rollback = position - 1
150
207
result = parseIdentifier ( )
151
208
if ( result === undefined ) {
152
209
position = rollback
210
+ endToken && endToken ( )
153
211
return undefined
154
212
} else {
213
+ endToken && endToken ( 'literal' , result )
155
214
return result
156
215
}
157
216
} else {
158
217
-- position
218
+ endToken && endToken ( )
159
219
return undefined
160
220
}
161
221
}
162
222
}
163
223
164
224
function skipWhiteSpace ( ) {
225
+ var insideWhiteSpace
226
+ function startWhiteSpace ( ) {
227
+ if ( ! insideWhiteSpace ) {
228
+ insideWhiteSpace = true
229
+ -- position
230
+ startToken ( )
231
+ ++ position
232
+ }
233
+ }
234
+ function endWhiteSpace ( ) {
235
+ if ( insideWhiteSpace ) {
236
+ insideWhiteSpace = false
237
+ endToken ( 'whitespace' )
238
+ }
239
+ }
165
240
while ( position < inputLength ) {
166
241
var char = input [ position ++ ]
167
242
if ( isLineTerminator ( char ) ) {
243
+ startToken && startWhiteSpace ( )
168
244
newLine ( char )
169
245
} else if ( isWhiteSpace ( char ) ) {
170
- // nothing
246
+ startToken && startWhiteSpace ( )
171
247
} else if ( char === '/' && ignoreComments &&
172
248
( input [ position ] === '/' || input [ position ] === '*' ) ) {
249
+ if ( startToken ) {
250
+ -- position
251
+ endWhiteSpace ( )
252
+ startToken ( )
253
+ ++ position
254
+ }
173
255
skipComment ( input [ position ++ ] === '*' )
256
+ endToken && endToken ( 'comment' )
174
257
} else {
175
258
-- position
176
259
break
177
260
}
178
261
}
262
+ endToken && endWhiteSpace ( )
179
263
}
180
264
181
265
function skipComment ( multiLine ) {
@@ -226,8 +310,9 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
226
310
fail ( 'Duplicate key: "' + key + '"' )
227
311
}
228
312
skipWhiteSpace ( )
229
-
313
+ startToken && startToken ( )
230
314
var char = input [ position ++ ]
315
+ endToken && endToken ( 'symbol' , char )
231
316
if ( char === '}' && key === undefined ) {
232
317
if ( ! ignoreTrailingCommas && isNotEmpty ) {
233
318
-- position
@@ -236,9 +321,9 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
236
321
return result
237
322
} else if ( char === ':' && key !== undefined ) {
238
323
skipWhiteSpace ( )
239
- stack . push ( key )
324
+ tokenPath && tokenPath . push ( key )
240
325
var value = parseGeneric ( )
241
- stack . pop ( )
326
+ tokenPath && tokenPath . pop ( )
242
327
243
328
if ( value === undefined ) fail ( 'No value found for key "' + key + '"' )
244
329
if ( typeof key !== 'string' ) {
@@ -260,7 +345,9 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
260
345
}
261
346
262
347
skipWhiteSpace ( )
348
+ startToken && startToken ( )
263
349
char = input [ position ++ ]
350
+ endToken && endToken ( 'symbol' , char )
264
351
if ( char === ',' ) {
265
352
continue
266
353
} else if ( char === '}' ) {
@@ -281,12 +368,13 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
281
368
var result = [ ]
282
369
while ( position < inputLength ) {
283
370
skipWhiteSpace ( )
284
- stack . push ( result . length )
371
+ tokenPath && tokenPath . push ( result . length )
285
372
var item = parseGeneric ( )
286
- stack . pop ( )
373
+ tokenPath && tokenPath . pop ( )
287
374
skipWhiteSpace ( )
288
-
375
+ startToken && startToken ( )
289
376
var char = input [ position ++ ]
377
+ endToken && endToken ( 'symbol' , char )
290
378
if ( item !== undefined ) {
291
379
if ( reviver ) {
292
380
item = reviver ( String ( result . length ) , item )
@@ -535,7 +623,7 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
535
623
if ( reviver ) {
536
624
returnValue = reviver ( '' , returnValue )
537
625
}
538
- return returnValue
626
+ return tokenize ? tokens : returnValue
539
627
} else {
540
628
fail ( )
541
629
}
@@ -547,3 +635,57 @@ function parseCustom (input, options) { // eslint-disable-line no-unused-vars
547
635
}
548
636
}
549
637
}
638
+
639
+ function parseCustom ( input , options ) { // eslint-disable-line no-unused-vars
640
+ if ( typeof options === 'function' ) {
641
+ options = {
642
+ reviver : options
643
+ }
644
+ } else if ( ! options ) {
645
+ options = { }
646
+ }
647
+ return parseInternal ( input , options )
648
+ }
649
+
650
+ function tokenize ( input , options ) { // eslint-disable-line no-unused-vars
651
+ if ( ! options ) {
652
+ options = { }
653
+ }
654
+ options . tokenize = true
655
+ return parseInternal ( input , options )
656
+ }
657
+
658
+ function escapePointerToken ( token ) {
659
+ return token
660
+ . toString ( )
661
+ . replace ( / ~ / g, '~0' )
662
+ . replace ( / \/ / g, '~1' )
663
+ }
664
+
665
+ function pathToPointer ( tokens ) { // eslint-disable-line no-unused-vars
666
+ if ( tokens . length === 0 ) {
667
+ return ''
668
+ }
669
+ return '/' + tokens
670
+ . map ( escapePointerToken )
671
+ . join ( '/' )
672
+ }
673
+
674
+ function unescapePointerToken ( token ) {
675
+ return token
676
+ . replace ( / ~ 1 / g, '/' )
677
+ . replace ( / ~ 0 / g, '~' )
678
+ }
679
+
680
+ function pointerToPath ( pointer ) { // eslint-disable-line no-unused-vars
681
+ if ( pointer === '' ) {
682
+ return [ ]
683
+ }
684
+ if ( pointer [ 0 ] !== '/' ) {
685
+ throw new Error ( 'Missing initial "/" in the reference' )
686
+ }
687
+ return pointer
688
+ . substr ( 1 )
689
+ . split ( '/' )
690
+ . map ( unescapePointerToken )
691
+ }
0 commit comments