OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** | 5 /// Translates a string of characters into a YAML serialization tree. |
6 * Translates a string of characters into a YAML serialization tree. | 6 /// |
7 * | 7 /// This parser is designed to closely follow the spec. All productions in the |
8 * This parser is designed to closely follow the spec. All productions in the | 8 /// spec are numbered, and the corresponding methods in the parser have the same |
9 * spec are numbered, and the corresponding methods in the parser have the same | 9 /// numbers. This is certainly not the most efficient way of parsing YAML, but |
10 * numbers. This is certainly not the most efficient way of parsing YAML, but it | 10 /// it is the easiest to write and read in the context of the spec. |
11 * is the easiest to write and read in the context of the spec. | 11 /// |
12 * | 12 /// Methods corresponding to productions are also named as in the spec, |
13 * Methods corresponding to productions are also named as in the spec, | 13 /// translating the name of the method (although not the annotation characters) |
14 * translating the name of the method (although not the annotation characters) | 14 /// into camel-case for dart style.. For example, the spec has a production |
15 * into camel-case for dart style.. For example, the spec has a production named | 15 /// named `nb-ns-plain-in-line`, and the method implementing it is named |
16 * `nb-ns-plain-in-line`, and the method implementing it is named | 16 /// `nb_ns_plainInLine`. The exception to that rule is methods that just |
17 * `nb_ns_plainInLine`. The exception to that rule is methods that just | 17 /// recognize character classes; these are named `is*`. |
18 * recognize character classes; these are named `is*`. | |
19 */ | |
20 class _Parser { | 18 class _Parser { |
21 static const TAB = 0x9; | 19 static const TAB = 0x9; |
22 static const LF = 0xA; | 20 static const LF = 0xA; |
23 static const CR = 0xD; | 21 static const CR = 0xD; |
24 static const SP = 0x20; | 22 static const SP = 0x20; |
25 static const TILDE = 0x7E; | 23 static const TILDE = 0x7E; |
26 static const NEL = 0x85; | 24 static const NEL = 0x85; |
27 static const PLUS = 0x2B; | 25 static const PLUS = 0x2B; |
28 static const HYPHEN = 0x2D; | 26 static const HYPHEN = 0x2D; |
29 static const QUESTION_MARK = 0x3F; | 27 static const QUESTION_MARK = 0x3F; |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
103 static const BLOCK_IN = 1; | 101 static const BLOCK_IN = 1; |
104 static const FLOW_OUT = 2; | 102 static const FLOW_OUT = 2; |
105 static const FLOW_IN = 3; | 103 static const FLOW_IN = 3; |
106 static const BLOCK_KEY = 4; | 104 static const BLOCK_KEY = 4; |
107 static const FLOW_KEY = 5; | 105 static const FLOW_KEY = 5; |
108 | 106 |
109 static const CHOMPING_STRIP = 0; | 107 static const CHOMPING_STRIP = 0; |
110 static const CHOMPING_KEEP = 1; | 108 static const CHOMPING_KEEP = 1; |
111 static const CHOMPING_CLIP = 2; | 109 static const CHOMPING_CLIP = 2; |
112 | 110 |
113 /** The source string being parsed. */ | 111 /// The source string being parsed. |
114 final String s; | 112 final String s; |
115 | 113 |
116 /** The current position in the source string. */ | 114 /// The current position in the source string. |
117 int pos = 0; | 115 int pos = 0; |
118 | 116 |
119 /** The length of the string being parsed. */ | 117 /// The length of the string being parsed. |
120 final int len; | 118 final int len; |
121 | 119 |
122 /** The current (0-based) line in the source string. */ | 120 /// The current (0-based) line in the source string. |
123 int line = 0; | 121 int line = 0; |
124 | 122 |
125 /** The current (0-based) column in the source string. */ | 123 /// The current (0-based) column in the source string. |
126 int column = 0; | 124 int column = 0; |
127 | 125 |
128 /** | 126 /// Whether we're parsing a bare document (that is, one that doesn't begin |
129 * Whether we're parsing a bare document (that is, one that doesn't begin with | 127 /// with `---`). Bare documents don't allow `%` immediately following |
130 * `---`). Bare documents don't allow `%` immediately following newlines. | 128 /// newlines. |
131 */ | |
132 bool inBareDocument = false; | 129 bool inBareDocument = false; |
133 | 130 |
134 /** | 131 /// The line number of the farthest position that has been parsed successfully |
135 * The line number of the farthest position that has been parsed successfully | 132 /// before backtracking. Used for error reporting. |
136 * before backtracking. Used for error reporting. | |
137 */ | |
138 int farthestLine = 0; | 133 int farthestLine = 0; |
139 | 134 |
140 /** | 135 /// The column number of the farthest position that has been parsed |
141 * The column number of the farthest position that has been parsed | 136 /// successfully before backtracking. Used for error reporting. |
142 * successfully before backtracking. Used for error reporting. | |
143 */ | |
144 int farthestColumn = 0; | 137 int farthestColumn = 0; |
145 | 138 |
146 /** | 139 /// The farthest position in the source string that has been parsed |
147 * The farthest position in the source string that has been parsed | 140 /// successfully before backtracking. Used for error reporting. |
148 * successfully before backtracking. Used for error reporting. | |
149 */ | |
150 int farthestPos = 0; | 141 int farthestPos = 0; |
151 | 142 |
152 /** | 143 /// The name of the context of the farthest position that has been parsed |
153 * The name of the context of the farthest position that has been parsed | 144 /// successfully before backtracking. Used for error reporting. |
154 * successfully before backtracking. Used for error reporting. | |
155 */ | |
156 String farthestContext = "document"; | 145 String farthestContext = "document"; |
157 | 146 |
158 /** A stack of the names of parse contexts. Used for error reporting. */ | 147 /// A stack of the names of parse contexts. Used for error reporting. |
159 List<String> contextStack; | 148 List<String> contextStack; |
160 | 149 |
161 /** | 150 /// Annotations attached to ranges of the source string that add extra |
162 * Annotations attached to ranges of the source string that add extra | 151 /// information to any errors that occur in the annotated range. |
163 * information to any errors that occur in the annotated range. | |
164 */ | |
165 _RangeMap<String> errorAnnotations; | 152 _RangeMap<String> errorAnnotations; |
166 | 153 |
167 /** | 154 /// The buffer containing the string currently being captured. |
168 * The buffer containing the string currently being captured. | |
169 */ | |
170 StringBuffer capturedString; | 155 StringBuffer capturedString; |
171 | 156 |
172 /** | 157 /// The beginning of the current section of the captured string. |
173 * The beginning of the current section of the captured string. | |
174 */ | |
175 int captureStart; | 158 int captureStart; |
176 | 159 |
177 /** | 160 /// Whether the current string capture is being overridden. |
178 * Whether the current string capture is being overridden. | |
179 */ | |
180 bool capturingAs = false; | 161 bool capturingAs = false; |
181 | 162 |
182 _Parser(String s) | 163 _Parser(String s) |
183 : this.s = s, | 164 : this.s = s, |
184 len = s.length, | 165 len = s.length, |
185 contextStack = <String>["document"], | 166 contextStack = <String>["document"], |
186 errorAnnotations = new _RangeMap(); | 167 errorAnnotations = new _RangeMap(); |
187 | 168 |
188 /** | 169 /// Return the character at the current position, then move that position |
189 * Return the character at the current position, then move that position | 170 /// forward one character. Also updates the current line and column numbers. |
190 * forward one character. Also updates the current line and column numbers. | |
191 */ | |
192 int next() { | 171 int next() { |
193 if (pos == len) return -1; | 172 if (pos == len) return -1; |
194 var char = s.charCodeAt(pos++); | 173 var char = s.charCodeAt(pos++); |
195 if (isBreak(char)) { | 174 if (isBreak(char)) { |
196 line++; | 175 line++; |
197 column = 0; | 176 column = 0; |
198 } else { | 177 } else { |
199 column++; | 178 column++; |
200 } | 179 } |
201 | 180 |
202 if (farthestLine < line) { | 181 if (farthestLine < line) { |
203 farthestLine = line; | 182 farthestLine = line; |
204 farthestColumn = column; | 183 farthestColumn = column; |
205 farthestContext = contextStack.last; | 184 farthestContext = contextStack.last; |
206 } else if (farthestLine == line && farthestColumn < column) { | 185 } else if (farthestLine == line && farthestColumn < column) { |
207 farthestColumn = column; | 186 farthestColumn = column; |
208 farthestContext = contextStack.last; | 187 farthestContext = contextStack.last; |
209 } | 188 } |
210 farthestPos = pos; | 189 farthestPos = pos; |
211 | 190 |
212 return char; | 191 return char; |
213 } | 192 } |
214 | 193 |
215 /** | 194 /// Returns the character at the current position, or the character [i] |
216 * Returns the character at the current position, or the character [i] | 195 /// characters after the current position. |
217 * characters after the current position. | 196 /// |
218 * | 197 /// Returns -1 if this would return a character after the end or before the |
219 * Returns -1 if this would return a character after the end or before the | 198 /// beginning of the input string. |
220 * beginning of the input string. | |
221 */ | |
222 int peek([int i = 0]) { | 199 int peek([int i = 0]) { |
223 var peekPos = pos + i; | 200 var peekPos = pos + i; |
224 return (peekPos >= len || peekPos < 0) ? -1 : s.charCodeAt(peekPos); | 201 return (peekPos >= len || peekPos < 0) ? -1 : s.charCodeAt(peekPos); |
225 } | 202 } |
226 | 203 |
227 /** | 204 /// The truthiness operator. Returns `false` if [obj] is `null` or `false`, |
228 * The truthiness operator. Returns `false` if [obj] is `null` or `false`, | 205 /// `true` otherwise. |
229 * `true` otherwise. | |
230 */ | |
231 bool truth(obj) => obj != null && obj != false; | 206 bool truth(obj) => obj != null && obj != false; |
232 | 207 |
233 /** | 208 /// Consumes the current character if it matches [matcher]. Returns the result |
234 * Consumes the current character if it matches [matcher]. Returns the result | 209 /// of [matcher]. |
235 * of [matcher]. | |
236 */ | |
237 bool consume(bool matcher(int)) { | 210 bool consume(bool matcher(int)) { |
238 if (matcher(peek())) { | 211 if (matcher(peek())) { |
239 next(); | 212 next(); |
240 return true; | 213 return true; |
241 } | 214 } |
242 return false; | 215 return false; |
243 } | 216 } |
244 | 217 |
245 /** | 218 /// Consumes the current character if it equals [char]. |
246 * Consumes the current character if it equals [char]. | |
247 */ | |
248 bool consumeChar(int char) => consume((c) => c == char); | 219 bool consumeChar(int char) => consume((c) => c == char); |
249 | 220 |
250 /** | 221 /// Calls [consumer] until it returns a falsey value. Returns a list of all |
251 * Calls [consumer] until it returns a falsey value. Returns a list of all | 222 /// truthy return values of [consumer], or null if it didn't consume anything. |
252 * truthy return values of [consumer], or null if it didn't consume anything. | 223 /// |
253 * | 224 /// Conceptually, repeats a production one or more times. |
254 * Conceptually, repeats a production one or more times. | |
255 */ | |
256 List oneOrMore(consumer()) { | 225 List oneOrMore(consumer()) { |
257 var first = consumer(); | 226 var first = consumer(); |
258 if (!truth(first)) return null; | 227 if (!truth(first)) return null; |
259 var out = [first]; | 228 var out = [first]; |
260 while (true) { | 229 while (true) { |
261 var el = consumer(); | 230 var el = consumer(); |
262 if (!truth(el)) return out; | 231 if (!truth(el)) return out; |
263 out.add(el); | 232 out.add(el); |
264 } | 233 } |
265 return null; // Unreachable. | 234 return null; // Unreachable. |
266 } | 235 } |
267 | 236 |
268 /** | 237 /// Calls [consumer] until it returns a falsey value. Returns a list of all |
269 * Calls [consumer] until it returns a falsey value. Returns a list of all | 238 /// truthy return values of [consumer], or the empty list if it didn't consume |
270 * truthy return values of [consumer], or the empty list if it didn't consume | 239 /// anything. |
271 * anything. | 240 /// |
272 * | 241 /// Conceptually, repeats a production any number of times. |
273 * Conceptually, repeats a production any number of times. | |
274 */ | |
275 List zeroOrMore(consumer()) { | 242 List zeroOrMore(consumer()) { |
276 var out = []; | 243 var out = []; |
277 var oldPos = pos; | 244 var oldPos = pos; |
278 while (true) { | 245 while (true) { |
279 var el = consumer(); | 246 var el = consumer(); |
280 if (!truth(el) || oldPos == pos) return out; | 247 if (!truth(el) || oldPos == pos) return out; |
281 oldPos = pos; | 248 oldPos = pos; |
282 out.add(el); | 249 out.add(el); |
283 } | 250 } |
284 return null; // Unreachable. | 251 return null; // Unreachable. |
285 } | 252 } |
286 | 253 |
287 /** | 254 /// Just calls [consumer] and returns its result. Used to make it explicit |
288 * Just calls [consumer] and returns its result. Used to make it explicit that | 255 /// that a production is intended to be optional. |
289 * a production is intended to be optional. | |
290 */ | |
291 zeroOrOne(consumer()) => consumer(); | 256 zeroOrOne(consumer()) => consumer(); |
292 | 257 |
293 /** | 258 /// Calls each function in [consumers] until one returns a truthy value, then |
294 * Calls each function in [consumers] until one returns a truthy value, then | 259 /// returns that. |
295 * returns that. | |
296 */ | |
297 or(List<Function> consumers) { | 260 or(List<Function> consumers) { |
298 for (var c in consumers) { | 261 for (var c in consumers) { |
299 var res = c(); | 262 var res = c(); |
300 if (truth(res)) return res; | 263 if (truth(res)) return res; |
301 } | 264 } |
302 return null; | 265 return null; |
303 } | 266 } |
304 | 267 |
305 /** | 268 /// Calls [consumer] and returns its result, but rolls back the parser state |
306 * Calls [consumer] and returns its result, but rolls back the parser state if | 269 /// if [consumer] returns a falsey value. |
307 * [consumer] returns a falsey value. | |
308 */ | |
309 transaction(consumer()) { | 270 transaction(consumer()) { |
310 var oldPos = pos; | 271 var oldPos = pos; |
311 var oldLine = line; | 272 var oldLine = line; |
312 var oldColumn = column; | 273 var oldColumn = column; |
313 var oldCaptureStart = captureStart; | 274 var oldCaptureStart = captureStart; |
314 String capturedSoFar = capturedString == null ? null : | 275 String capturedSoFar = capturedString == null ? null : |
315 capturedString.toString(); | 276 capturedString.toString(); |
316 var res = consumer(); | 277 var res = consumer(); |
317 if (truth(res)) return res; | 278 if (truth(res)) return res; |
318 | 279 |
319 pos = oldPos; | 280 pos = oldPos; |
320 line = oldLine; | 281 line = oldLine; |
321 column = oldColumn; | 282 column = oldColumn; |
322 captureStart = oldCaptureStart; | 283 captureStart = oldCaptureStart; |
323 capturedString = capturedSoFar == null ? null : | 284 capturedString = capturedSoFar == null ? null : |
324 new StringBuffer(capturedSoFar); | 285 new StringBuffer(capturedSoFar); |
325 return res; | 286 return res; |
326 } | 287 } |
327 | 288 |
328 /** | 289 /// Consumes [n] characters matching [matcher], or none if there isn't a |
329 * Consumes [n] characters matching [matcher], or none if there isn't a | 290 /// complete match. The first argument to [matcher] is the character code, the |
330 * complete match. The first argument to [matcher] is the character code, the | 291 /// second is the index (from 0 to [n] - 1). |
331 * second is the index (from 0 to [n] - 1). | 292 /// |
332 * | 293 /// Returns whether or not the characters were consumed. |
333 * Returns whether or not the characters were consumed. | |
334 */ | |
335 bool nAtOnce(int n, bool matcher(int c, int i)) => transaction(() { | 294 bool nAtOnce(int n, bool matcher(int c, int i)) => transaction(() { |
336 for (int i = 0; i < n; i++) { | 295 for (int i = 0; i < n; i++) { |
337 if (!consume((c) => matcher(c, i))) return false; | 296 if (!consume((c) => matcher(c, i))) return false; |
338 } | 297 } |
339 return true; | 298 return true; |
340 }); | 299 }); |
341 | 300 |
342 /** | 301 /// Consumes the exact characters in [str], or nothing. |
343 * Consumes the exact characters in [str], or nothing. | 302 /// |
344 * | 303 /// Returns whether or not the string was consumed. |
345 * Returns whether or not the string was consumed. | |
346 */ | |
347 bool rawString(String str) => | 304 bool rawString(String str) => |
348 nAtOnce(str.length, (c, i) => str.charCodeAt(i) == c); | 305 nAtOnce(str.length, (c, i) => str.charCodeAt(i) == c); |
349 | 306 |
350 /** | 307 /// Consumes and returns a string of characters matching [matcher], or null if |
351 * Consumes and returns a string of characters matching [matcher], or null if | 308 /// there are no such characters. |
352 * there are no such characters. | |
353 */ | |
354 String stringOf(bool matcher(int)) => | 309 String stringOf(bool matcher(int)) => |
355 captureString(() => oneOrMore(() => consume(matcher))); | 310 captureString(() => oneOrMore(() => consume(matcher))); |
356 | 311 |
357 /** | 312 /// Calls [consumer] and returns the string that was consumed while doing so, |
358 * Calls [consumer] and returns the string that was consumed while doing so, | 313 /// or null if [consumer] returned a falsey value. Automatically wraps |
359 * or null if [consumer] returned a falsey value. Automatically wraps | 314 /// [consumer] in `transaction`. |
360 * [consumer] in `transaction`. | |
361 */ | |
362 String captureString(consumer()) { | 315 String captureString(consumer()) { |
363 // captureString calls may not be nested | 316 // captureString calls may not be nested |
364 assert(capturedString == null); | 317 assert(capturedString == null); |
365 | 318 |
366 captureStart = pos; | 319 captureStart = pos; |
367 capturedString = new StringBuffer(); | 320 capturedString = new StringBuffer(); |
368 var res = transaction(consumer); | 321 var res = transaction(consumer); |
369 if (!truth(res)) { | 322 if (!truth(res)) { |
370 captureStart = null; | 323 captureStart = null; |
371 capturedString = null; | 324 capturedString = null; |
(...skipping 23 matching lines...) Expand all Loading... |
395 capturedString.add(transformation(s.substring(captureStart, pos))); | 348 capturedString.add(transformation(s.substring(captureStart, pos))); |
396 captureStart = pos; | 349 captureStart = pos; |
397 return res; | 350 return res; |
398 } | 351 } |
399 | 352 |
400 void flushCapture() { | 353 void flushCapture() { |
401 capturedString.add(s.substring(captureStart, pos)); | 354 capturedString.add(s.substring(captureStart, pos)); |
402 captureStart = pos; | 355 captureStart = pos; |
403 } | 356 } |
404 | 357 |
405 /** | 358 /// Adds a tag and an anchor to [node], if they're defined. |
406 * Adds a tag and an anchor to [node], if they're defined. | |
407 */ | |
408 _Node addProps(_Node node, _Pair<_Tag, String> props) { | 359 _Node addProps(_Node node, _Pair<_Tag, String> props) { |
409 if (props == null || node == null) return node; | 360 if (props == null || node == null) return node; |
410 if (truth(props.first)) node.tag = props.first; | 361 if (truth(props.first)) node.tag = props.first; |
411 if (truth(props.last)) node.anchor = props.last; | 362 if (truth(props.last)) node.anchor = props.last; |
412 return node; | 363 return node; |
413 } | 364 } |
414 | 365 |
415 /** Creates a MappingNode from [pairs]. */ | 366 /// Creates a MappingNode from [pairs]. |
416 _MappingNode map(List<_Pair<_Node, _Node>> pairs) { | 367 _MappingNode map(List<_Pair<_Node, _Node>> pairs) { |
417 var content = new Map<_Node, _Node>(); | 368 var content = new Map<_Node, _Node>(); |
418 pairs.forEach((pair) => content[pair.first] = pair.last); | 369 pairs.forEach((pair) => content[pair.first] = pair.last); |
419 return new _MappingNode("?", content); | 370 return new _MappingNode("?", content); |
420 } | 371 } |
421 | 372 |
422 /** Runs [fn] in a context named [name]. Used for error reporting. */ | 373 /// Runs [fn] in a context named [name]. Used for error reporting. |
423 context(String name, fn()) { | 374 context(String name, fn()) { |
424 try { | 375 try { |
425 contextStack.add(name); | 376 contextStack.add(name); |
426 return fn(); | 377 return fn(); |
427 } finally { | 378 } finally { |
428 var popped = contextStack.removeLast(); | 379 var popped = contextStack.removeLast(); |
429 assert(popped == name); | 380 assert(popped == name); |
430 } | 381 } |
431 } | 382 } |
432 | 383 |
433 /** | 384 /// Adds [message] as extra information to any errors that occur between the |
434 * Adds [message] as extra information to any errors that occur between the | 385 /// current position and the position of the cursor after running [fn]. The |
435 * current position and the position of the cursor after running [fn]. The | 386 /// cursor is reset after [fn] is run. |
436 * cursor is reset after [fn] is run. | |
437 */ | |
438 annotateError(String message, fn()) { | 387 annotateError(String message, fn()) { |
439 var start = pos; | 388 var start = pos; |
440 var end; | 389 var end; |
441 transaction(() { | 390 transaction(() { |
442 fn(); | 391 fn(); |
443 end = pos; | 392 end = pos; |
444 return false; | 393 return false; |
445 }); | 394 }); |
446 errorAnnotations[new _Range(start, end)] = message; | 395 errorAnnotations[new _Range(start, end)] = message; |
447 } | 396 } |
448 | 397 |
449 /** Throws an error with additional context information. */ | 398 /// Throws an error with additional context information. |
450 error(String message) { | 399 error(String message) { |
451 // Line and column should be one-based. | 400 // Line and column should be one-based. |
452 throw new SyntaxError(line + 1, column + 1, | 401 throw new SyntaxError(line + 1, column + 1, |
453 "$message (in $farthestContext)"); | 402 "$message (in $farthestContext)"); |
454 } | 403 } |
455 | 404 |
456 /** | 405 /// If [result] is falsey, throws an error saying that [expected] was |
457 * If [result] is falsey, throws an error saying that [expected] was | 406 /// expected. |
458 * expected. | |
459 */ | |
460 expect(result, String expected) { | 407 expect(result, String expected) { |
461 if (truth(result)) return result; | 408 if (truth(result)) return result; |
462 error("expected $expected"); | 409 error("expected $expected"); |
463 } | 410 } |
464 | 411 |
465 /** | 412 /// Throws an error saying that the parse failed. Uses [farthestLine], |
466 * Throws an error saying that the parse failed. Uses [farthestLine], | 413 /// [farthestColumn], and [farthestContext] to provide additional information. |
467 * [farthestColumn], and [farthestContext] to provide additional information. | |
468 */ | |
469 parseFailed() { | 414 parseFailed() { |
470 var message = "invalid YAML in $farthestContext"; | 415 var message = "invalid YAML in $farthestContext"; |
471 var extraError = errorAnnotations[farthestPos]; | 416 var extraError = errorAnnotations[farthestPos]; |
472 if (extraError != null) message = "$message ($extraError)"; | 417 if (extraError != null) message = "$message ($extraError)"; |
473 throw new SyntaxError(farthestLine + 1, farthestColumn + 1, message); | 418 throw new SyntaxError(farthestLine + 1, farthestColumn + 1, message); |
474 } | 419 } |
475 | 420 |
476 /** Returns the number of spaces after the current position. */ | 421 /// Returns the number of spaces after the current position. |
477 int countIndentation() { | 422 int countIndentation() { |
478 var i = 0; | 423 var i = 0; |
479 while (peek(i) == SP) i++; | 424 while (peek(i) == SP) i++; |
480 return i; | 425 return i; |
481 } | 426 } |
482 | 427 |
483 /** Returns the indentation for a block scalar. */ | 428 /// Returns the indentation for a block scalar. |
484 int blockScalarAdditionalIndentation(_BlockHeader header, int indent) { | 429 int blockScalarAdditionalIndentation(_BlockHeader header, int indent) { |
485 if (!header.autoDetectIndent) return header.additionalIndent; | 430 if (!header.autoDetectIndent) return header.additionalIndent; |
486 | 431 |
487 var maxSpaces = 0; | 432 var maxSpaces = 0; |
488 var maxSpacesLine = 0; | 433 var maxSpacesLine = 0; |
489 var spaces = 0; | 434 var spaces = 0; |
490 transaction(() { | 435 transaction(() { |
491 do { | 436 do { |
492 spaces = captureString(() => zeroOrMore(() => consumeChar(SP))).length; | 437 spaces = captureString(() => zeroOrMore(() => consumeChar(SP))).length; |
493 if (spaces > maxSpaces) { | 438 if (spaces > maxSpaces) { |
(...skipping 13 matching lines...) Expand all Loading... |
507 // non-empty line. | 452 // non-empty line. |
508 if (maxSpaces > spaces) { | 453 if (maxSpaces > spaces) { |
509 throw new SyntaxError(maxSpacesLine + 1, maxSpaces, | 454 throw new SyntaxError(maxSpacesLine + 1, maxSpaces, |
510 "Leading empty lines may not be indented more than the first " | 455 "Leading empty lines may not be indented more than the first " |
511 "non-empty line."); | 456 "non-empty line."); |
512 } | 457 } |
513 | 458 |
514 return spaces - indent; | 459 return spaces - indent; |
515 } | 460 } |
516 | 461 |
517 /** Returns whether the current position is at the beginning of a line. */ | 462 /// Returns whether the current position is at the beginning of a line. |
518 bool get atStartOfLine => column == 0; | 463 bool get atStartOfLine => column == 0; |
519 | 464 |
520 /** Returns whether the current position is at the end of the input. */ | 465 /// Returns whether the current position is at the end of the input. |
521 bool get atEndOfFile => pos == len; | 466 bool get atEndOfFile => pos == len; |
522 | 467 |
523 /** | 468 /// Given an indicator character, returns the type of that indicator (or null |
524 * Given an indicator character, returns the type of that indicator (or null | 469 /// if the indicator isn't found. |
525 * if the indicator isn't found. | |
526 */ | |
527 int indicatorType(int char) { | 470 int indicatorType(int char) { |
528 switch (char) { | 471 switch (char) { |
529 case HYPHEN: return C_SEQUENCE_ENTRY; | 472 case HYPHEN: return C_SEQUENCE_ENTRY; |
530 case QUESTION_MARK: return C_MAPPING_KEY; | 473 case QUESTION_MARK: return C_MAPPING_KEY; |
531 case COLON: return C_MAPPING_VALUE; | 474 case COLON: return C_MAPPING_VALUE; |
532 case COMMA: return C_COLLECT_ENTRY; | 475 case COMMA: return C_COLLECT_ENTRY; |
533 case LEFT_BRACKET: return C_SEQUENCE_START; | 476 case LEFT_BRACKET: return C_SEQUENCE_START; |
534 case RIGHT_BRACKET: return C_SEQUENCE_END; | 477 case RIGHT_BRACKET: return C_SEQUENCE_END; |
535 case LEFT_BRACE: return C_MAPPING_START; | 478 case LEFT_BRACE: return C_MAPPING_START; |
536 case RIGHT_BRACE: return C_MAPPING_END; | 479 case RIGHT_BRACE: return C_MAPPING_END; |
(...skipping 1387 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1924 | 1867 |
1925 class SyntaxError extends YamlException { | 1868 class SyntaxError extends YamlException { |
1926 final int line; | 1869 final int line; |
1927 final int column; | 1870 final int column; |
1928 | 1871 |
1929 SyntaxError(this.line, this.column, String msg) : super(msg); | 1872 SyntaxError(this.line, this.column, String msg) : super(msg); |
1930 | 1873 |
1931 String toString() => "Syntax error on line $line, column $column: $msg"; | 1874 String toString() => "Syntax error on line $line, column $column: $msg"; |
1932 } | 1875 } |
1933 | 1876 |
1934 /** A pair of values. */ | 1877 /// A pair of values. |
1935 class _Pair<E, F> { | 1878 class _Pair<E, F> { |
1936 E first; | 1879 E first; |
1937 F last; | 1880 F last; |
1938 | 1881 |
1939 _Pair(this.first, this.last); | 1882 _Pair(this.first, this.last); |
1940 | 1883 |
1941 String toString() => '($first, $last)'; | 1884 String toString() => '($first, $last)'; |
1942 } | 1885 } |
1943 | 1886 |
1944 /** The information in the header for a block scalar. */ | 1887 /// The information in the header for a block scalar. |
1945 class _BlockHeader { | 1888 class _BlockHeader { |
1946 final int additionalIndent; | 1889 final int additionalIndent; |
1947 final int chomping; | 1890 final int chomping; |
1948 | 1891 |
1949 _BlockHeader(this.additionalIndent, this.chomping); | 1892 _BlockHeader(this.additionalIndent, this.chomping); |
1950 | 1893 |
1951 bool get autoDetectIndent => additionalIndent == null; | 1894 bool get autoDetectIndent => additionalIndent == null; |
1952 } | 1895 } |
1953 | 1896 |
1954 /** | 1897 /// A range of characters in the YAML document, from [start] to [end] |
1955 * A range of characters in the YAML document, from [start] to [end] (inclusive)
. | 1898 /// (inclusive). |
1956 */ | |
1957 class _Range { | 1899 class _Range { |
1958 /** The first character in the range. */ | 1900 /// The first character in the range. |
1959 final int start; | 1901 final int start; |
1960 | 1902 |
1961 /** The last character in the range. */ | 1903 /// The last character in the range. |
1962 final int end; | 1904 final int end; |
1963 | 1905 |
1964 _Range(this.start, this.end); | 1906 _Range(this.start, this.end); |
1965 | 1907 |
1966 /** Returns whether or not [pos] lies within this range. */ | 1908 /// Returns whether or not [pos] lies within this range. |
1967 bool contains(int pos) => pos >= start && pos <= end; | 1909 bool contains(int pos) => pos >= start && pos <= end; |
1968 } | 1910 } |
1969 | 1911 |
1970 /** | 1912 /// A map that associates [E] values with [_Range]s. It's efficient to create |
1971 * A map that associates [E] values with [_Range]s. It's efficient to create new | 1913 /// new associations, but finding the value associated with a position is more |
1972 * associations, but finding the value associated with a position is more | 1914 /// expensive. |
1973 * expensive. | |
1974 */ | |
1975 class _RangeMap<E> { | 1915 class _RangeMap<E> { |
1976 /** The ranges and their associated elements. */ | 1916 /// The ranges and their associated elements. |
1977 final List<_Pair<_Range, E>> contents; | 1917 final List<_Pair<_Range, E>> contents; |
1978 | 1918 |
1979 _RangeMap() : this.contents = <_Pair<_Range, E>>[]; | 1919 _RangeMap() : this.contents = <_Pair<_Range, E>>[]; |
1980 | 1920 |
1981 /** | 1921 /// Returns the value associated with the range in which [pos] lies, or null |
1982 * Returns the value associated with the range in which [pos] lies, or null if | 1922 /// if there is no such range. If there's more than one such range, the most |
1983 * there is no such range. If there's more than one such range, the most | 1923 /// recently set one is used. |
1984 * recently set one is used. | |
1985 */ | |
1986 E operator[](int pos) { | 1924 E operator[](int pos) { |
1987 // Iterate backwards through contents so the more recent range takes | 1925 // Iterate backwards through contents so the more recent range takes |
1988 // precedence. TODO(nweiz): clean this up when issue 2804 is fixed. | 1926 // precedence. TODO(nweiz): clean this up when issue 2804 is fixed. |
1989 for (var i = contents.length - 1; i >= 0; i--) { | 1927 for (var i = contents.length - 1; i >= 0; i--) { |
1990 var pair = contents[i]; | 1928 var pair = contents[i]; |
1991 if (pair.first.contains(pos)) return pair.last; | 1929 if (pair.first.contains(pos)) return pair.last; |
1992 } | 1930 } |
1993 return null; | 1931 return null; |
1994 } | 1932 } |
1995 | 1933 |
1996 /** Associates [value] with [range]. */ | 1934 /// Associates [value] with [range]. |
1997 operator[]=(_Range range, E value) => | 1935 operator[]=(_Range range, E value) => |
1998 contents.add(new _Pair<_Range, E>(range, value)); | 1936 contents.add(new _Pair<_Range, E>(range, value)); |
1999 } | 1937 } |
OLD | NEW |