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 library yaml.parser; | 5 library yaml.parser; |
6 | 6 |
7 import 'dart:collection'; | 7 import 'dart:collection'; |
8 | 8 |
| 9 import 'package:string_scanner/string_scanner.dart'; |
| 10 |
9 import 'model.dart'; | 11 import 'model.dart'; |
10 import 'yaml_exception.dart'; | 12 import 'yaml_exception.dart'; |
11 import 'yaml_map.dart'; | 13 import 'yaml_map.dart'; |
12 | 14 |
13 /// Translates a string of characters into a YAML serialization tree. | 15 /// Translates a string of characters into a YAML serialization tree. |
14 /// | 16 /// |
15 /// This parser is designed to closely follow the spec. All productions in the | 17 /// This parser is designed to closely follow the spec. All productions in the |
16 /// spec are numbered, and the corresponding methods in the parser have the same | 18 /// spec are numbered, and the corresponding methods in the parser have the same |
17 /// numbers. This is certainly not the most efficient way of parsing YAML, but | 19 /// numbers. This is certainly not the most efficient way of parsing YAML, but |
18 /// it is the easiest to write and read in the context of the spec. | 20 /// it is the easiest to write and read in the context of the spec. |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 static const BLOCK_IN = 1; | 111 static const BLOCK_IN = 1; |
110 static const FLOW_OUT = 2; | 112 static const FLOW_OUT = 2; |
111 static const FLOW_IN = 3; | 113 static const FLOW_IN = 3; |
112 static const BLOCK_KEY = 4; | 114 static const BLOCK_KEY = 4; |
113 static const FLOW_KEY = 5; | 115 static const FLOW_KEY = 5; |
114 | 116 |
115 static const CHOMPING_STRIP = 0; | 117 static const CHOMPING_STRIP = 0; |
116 static const CHOMPING_KEEP = 1; | 118 static const CHOMPING_KEEP = 1; |
117 static const CHOMPING_CLIP = 2; | 119 static const CHOMPING_CLIP = 2; |
118 | 120 |
119 /// The source string being parsed. | 121 /// The scanner that's used to scan through the document. |
120 final String _s; | 122 final SpanScanner _scanner; |
121 | |
122 /// The current position in the source string. | |
123 int _pos = 0; | |
124 | |
125 /// The length of the string being parsed. | |
126 final int _len; | |
127 | |
128 /// The current (0-based) line in the source string. | |
129 int _line = 0; | |
130 | |
131 /// The current (0-based) column in the source string. | |
132 int _column = 0; | |
133 | 123 |
134 /// Whether we're parsing a bare document (that is, one that doesn't begin | 124 /// Whether we're parsing a bare document (that is, one that doesn't begin |
135 /// with `---`). Bare documents don't allow `%` immediately following | 125 /// with `---`). Bare documents don't allow `%` immediately following |
136 /// newlines. | 126 /// newlines. |
137 bool _inBareDocument = false; | 127 bool _inBareDocument = false; |
138 | 128 |
139 /// The line number of the farthest position that has been parsed successfully | 129 /// The state of the scanner when it was the farthest in the document it's |
140 /// before backtracking. Used for error reporting. | 130 /// been. |
141 int _farthestLine = 0; | 131 LineScannerState _farthestState; |
142 | |
143 /// The column number of the farthest position that has been parsed | |
144 /// successfully before backtracking. Used for error reporting. | |
145 int _farthestColumn = 0; | |
146 | |
147 /// The farthest position in the source string that has been parsed | |
148 /// successfully before backtracking. Used for error reporting. | |
149 int _farthestPos = 0; | |
150 | 132 |
151 /// The name of the context of the farthest position that has been parsed | 133 /// The name of the context of the farthest position that has been parsed |
152 /// successfully before backtracking. Used for error reporting. | 134 /// successfully before backtracking. Used for error reporting. |
153 String _farthestContext = "document"; | 135 String _farthestContext = "document"; |
154 | 136 |
155 /// A stack of the names of parse contexts. Used for error reporting. | 137 /// A stack of the names of parse contexts. Used for error reporting. |
156 List<String> _contextStack; | 138 final _contextStack = <String>["document"]; |
157 | 139 |
158 /// Annotations attached to ranges of the source string that add extra | 140 /// Annotations attached to ranges of the source string that add extra |
159 /// information to any errors that occur in the annotated range. | 141 /// information to any errors that occur in the annotated range. |
160 _RangeMap<String> _errorAnnotations; | 142 final _errorAnnotations = new _RangeMap<String>(); |
161 | 143 |
162 /// The buffer containing the string currently being captured. | 144 /// The buffer containing the string currently being captured. |
163 StringBuffer _capturedString; | 145 StringBuffer _capturedString; |
164 | 146 |
165 /// The beginning of the current section of the captured string. | 147 /// The beginning of the current section of the captured string. |
166 int _captureStart; | 148 int _captureStart; |
167 | 149 |
168 /// Whether the current string capture is being overridden. | 150 /// Whether the current string capture is being overridden. |
169 bool _capturingAs = false; | 151 bool _capturingAs = false; |
170 | 152 |
171 Parser(String s) | 153 Parser(String yaml, String sourceName) |
172 : this._s = s, | 154 : _scanner = new SpanScanner(yaml, sourceName) { |
173 _len = s.length, | 155 _farthestState = _scanner.state; |
174 _contextStack = <String>["document"], | 156 } |
175 _errorAnnotations = new _RangeMap(); | |
176 | 157 |
177 /// Return the character at the current position, then move that position | 158 /// Returns the character at the current position, then moves that position |
178 /// forward one character. Also updates the current line and column numbers. | 159 /// forward one character. |
179 int next() { | 160 int next() => _scanner.readChar(); |
180 if (_pos == _len) return -1; | |
181 var char = _s.codeUnitAt(_pos++); | |
182 if (isBreak(char)) { | |
183 _line++; | |
184 _column = 0; | |
185 } else { | |
186 _column++; | |
187 } | |
188 | |
189 if (_farthestLine < _line) { | |
190 _farthestLine = _line; | |
191 _farthestColumn = _column; | |
192 _farthestContext = _contextStack.last; | |
193 } else if (_farthestLine == _line && _farthestColumn < _column) { | |
194 _farthestColumn = _column; | |
195 _farthestContext = _contextStack.last; | |
196 } | |
197 _farthestPos = _pos; | |
198 | |
199 return char; | |
200 } | |
201 | 161 |
202 /// Returns the code unit at the current position, or the character [i] | 162 /// Returns the code unit at the current position, or the character [i] |
203 /// characters after the current position. | 163 /// characters after the current position. |
204 /// | 164 int peek([int i = 0]) => _scanner.peekChar(i); |
205 /// Returns -1 if this would return a character after the end or before the | |
206 /// beginning of the input string. | |
207 int peek([int i = 0]) { | |
208 var peekPos = _pos + i; | |
209 return (peekPos >= _len || peekPos < 0) ? -1 : _s.codeUnitAt(peekPos); | |
210 } | |
211 | 165 |
212 /// The truthiness operator. Returns `false` if [obj] is `null` or `false`, | 166 /// The truthiness operator. Returns `false` if [obj] is `null` or `false`, |
213 /// `true` otherwise. | 167 /// `true` otherwise. |
214 bool truth(obj) => obj != null && obj != false; | 168 bool truth(obj) => obj != null && obj != false; |
215 | 169 |
216 /// Consumes the current character if it matches [matcher]. Returns the result | 170 /// Consumes the current character if it matches [matcher]. Returns the result |
217 /// of [matcher]. | 171 /// of [matcher]. |
218 bool consume(bool matcher(int)) { | 172 bool consume(bool matcher(int)) { |
219 if (matcher(peek())) { | 173 if (matcher(peek())) { |
220 next(); | 174 next(); |
(...skipping 21 matching lines...) Expand all Loading... |
242 return null; // Unreachable. | 196 return null; // Unreachable. |
243 } | 197 } |
244 | 198 |
245 /// Calls [consumer] until it returns a falsey value. Returns a list of all | 199 /// Calls [consumer] until it returns a falsey value. Returns a list of all |
246 /// truthy return values of [consumer], or the empty list if it didn't consume | 200 /// truthy return values of [consumer], or the empty list if it didn't consume |
247 /// anything. | 201 /// anything. |
248 /// | 202 /// |
249 /// Conceptually, repeats a production any number of times. | 203 /// Conceptually, repeats a production any number of times. |
250 List zeroOrMore(consumer()) { | 204 List zeroOrMore(consumer()) { |
251 var out = []; | 205 var out = []; |
252 var oldPos = _pos; | 206 var oldPos = _scanner.position; |
253 while (true) { | 207 while (true) { |
254 var el = consumer(); | 208 var el = consumer(); |
255 if (!truth(el) || oldPos == _pos) return out; | 209 if (!truth(el) || oldPos == _scanner.position) return out; |
256 oldPos = _pos; | 210 oldPos = _scanner.position; |
257 out.add(el); | 211 out.add(el); |
258 } | 212 } |
259 return null; // Unreachable. | 213 return null; // Unreachable. |
260 } | 214 } |
261 | 215 |
262 /// Just calls [consumer] and returns its result. Used to make it explicit | 216 /// Just calls [consumer] and returns its result. Used to make it explicit |
263 /// that a production is intended to be optional. | 217 /// that a production is intended to be optional. |
264 zeroOrOne(consumer()) => consumer(); | 218 zeroOrOne(consumer()) => consumer(); |
265 | 219 |
266 /// Calls each function in [consumers] until one returns a truthy value, then | 220 /// Calls each function in [consumers] until one returns a truthy value, then |
267 /// returns that. | 221 /// returns that. |
268 or(List<Function> consumers) { | 222 or(List<Function> consumers) { |
269 for (var c in consumers) { | 223 for (var c in consumers) { |
270 var res = c(); | 224 var res = c(); |
271 if (truth(res)) return res; | 225 if (truth(res)) return res; |
272 } | 226 } |
273 return null; | 227 return null; |
274 } | 228 } |
275 | 229 |
276 /// Calls [consumer] and returns its result, but rolls back the parser state | 230 /// Calls [consumer] and returns its result, but rolls back the parser state |
277 /// if [consumer] returns a falsey value. | 231 /// if [consumer] returns a falsey value. |
278 transaction(consumer()) { | 232 transaction(consumer()) { |
279 var oldPos = _pos; | 233 var oldState = _scanner.state; |
280 var oldLine = _line; | |
281 var oldColumn = _column; | |
282 var oldCaptureStart = _captureStart; | 234 var oldCaptureStart = _captureStart; |
283 String capturedSoFar = _capturedString == null ? null : | 235 String capturedSoFar = _capturedString == null ? null : |
284 _capturedString.toString(); | 236 _capturedString.toString(); |
285 var res = consumer(); | 237 var res = consumer(); |
| 238 _refreshFarthestState(); |
286 if (truth(res)) return res; | 239 if (truth(res)) return res; |
287 | 240 |
288 _pos = oldPos; | 241 _scanner.state = oldState; |
289 _line = oldLine; | |
290 _column = oldColumn; | |
291 _captureStart = oldCaptureStart; | 242 _captureStart = oldCaptureStart; |
292 _capturedString = capturedSoFar == null ? null : | 243 _capturedString = capturedSoFar == null ? null : |
293 new StringBuffer(capturedSoFar); | 244 new StringBuffer(capturedSoFar); |
294 return res; | 245 return res; |
295 } | 246 } |
296 | 247 |
297 /// Consumes [n] characters matching [matcher], or none if there isn't a | 248 /// Consumes [n] characters matching [matcher], or none if there isn't a |
298 /// complete match. The first argument to [matcher] is the character code, the | 249 /// complete match. The first argument to [matcher] is the character code, the |
299 /// second is the index (from 0 to [n] - 1). | 250 /// second is the index (from 0 to [n] - 1). |
300 /// | 251 /// |
(...skipping 16 matching lines...) Expand all Loading... |
317 String stringOf(bool matcher(int)) => | 268 String stringOf(bool matcher(int)) => |
318 captureString(() => oneOrMore(() => consume(matcher))); | 269 captureString(() => oneOrMore(() => consume(matcher))); |
319 | 270 |
320 /// Calls [consumer] and returns the string that was consumed while doing so, | 271 /// Calls [consumer] and returns the string that was consumed while doing so, |
321 /// or null if [consumer] returned a falsey value. Automatically wraps | 272 /// or null if [consumer] returned a falsey value. Automatically wraps |
322 /// [consumer] in `transaction`. | 273 /// [consumer] in `transaction`. |
323 String captureString(consumer()) { | 274 String captureString(consumer()) { |
324 // captureString calls may not be nested | 275 // captureString calls may not be nested |
325 assert(_capturedString == null); | 276 assert(_capturedString == null); |
326 | 277 |
327 _captureStart = _pos; | 278 _captureStart = _scanner.position; |
328 _capturedString = new StringBuffer(); | 279 _capturedString = new StringBuffer(); |
329 var res = transaction(consumer); | 280 var res = transaction(consumer); |
330 if (!truth(res)) { | 281 if (!truth(res)) { |
331 _captureStart = null; | 282 _captureStart = null; |
332 _capturedString = null; | 283 _capturedString = null; |
333 return null; | 284 return null; |
334 } | 285 } |
335 | 286 |
336 flushCapture(); | 287 flushCapture(); |
337 var result = _capturedString.toString(); | 288 var result = _capturedString.toString(); |
338 _captureStart = null; | 289 _captureStart = null; |
339 _capturedString = null; | 290 _capturedString = null; |
340 return result; | 291 return result; |
341 } | 292 } |
342 | 293 |
343 captureAs(String replacement, consumer()) => | 294 captureAs(String replacement, consumer()) => |
344 captureAndTransform(consumer, (_) => replacement); | 295 captureAndTransform(consumer, (_) => replacement); |
345 | 296 |
346 captureAndTransform(consumer(), String transformation(String captured)) { | 297 captureAndTransform(consumer(), String transformation(String captured)) { |
347 if (_capturedString == null) return consumer(); | 298 if (_capturedString == null) return consumer(); |
348 if (_capturingAs) return consumer(); | 299 if (_capturingAs) return consumer(); |
349 | 300 |
350 flushCapture(); | 301 flushCapture(); |
351 _capturingAs = true; | 302 _capturingAs = true; |
352 var res = consumer(); | 303 var res = consumer(); |
353 _capturingAs = false; | 304 _capturingAs = false; |
354 if (!truth(res)) return res; | 305 if (!truth(res)) return res; |
355 | 306 |
356 _capturedString.write(transformation(_s.substring(_captureStart, _pos))); | 307 _capturedString.write(transformation( |
357 _captureStart = _pos; | 308 _scanner.string.substring(_captureStart, _scanner.position))); |
| 309 _captureStart = _scanner.position; |
358 return res; | 310 return res; |
359 } | 311 } |
360 | 312 |
361 void flushCapture() { | 313 void flushCapture() { |
362 _capturedString.write(_s.substring(_captureStart, _pos)); | 314 _capturedString.write(_scanner.string.substring( |
363 _captureStart = _pos; | 315 _captureStart, _scanner.position)); |
| 316 _captureStart = _scanner.position; |
364 } | 317 } |
365 | 318 |
366 /// Adds a tag and an anchor to [node], if they're defined. | 319 /// Adds a tag and an anchor to [node], if they're defined. |
367 Node addProps(Node node, _Pair<Tag, String> props) { | 320 Node addProps(Node node, _Pair<Tag, String> props) { |
368 if (props == null || node == null) return node; | 321 if (props == null || node == null) return node; |
369 if (truth(props.first)) node.tag = props.first; | 322 if (truth(props.first)) node.tag = props.first; |
370 if (truth(props.last)) node.anchor = props.last; | 323 if (truth(props.last)) node.anchor = props.last; |
371 return node; | 324 return node; |
372 } | 325 } |
373 | 326 |
(...skipping 12 matching lines...) Expand all Loading... |
386 } finally { | 339 } finally { |
387 var popped = _contextStack.removeLast(); | 340 var popped = _contextStack.removeLast(); |
388 assert(popped == name); | 341 assert(popped == name); |
389 } | 342 } |
390 } | 343 } |
391 | 344 |
392 /// Adds [message] as extra information to any errors that occur between the | 345 /// Adds [message] as extra information to any errors that occur between the |
393 /// current position and the position of the cursor after running [fn]. The | 346 /// current position and the position of the cursor after running [fn]. The |
394 /// cursor is reset after [fn] is run. | 347 /// cursor is reset after [fn] is run. |
395 annotateError(String message, fn()) { | 348 annotateError(String message, fn()) { |
396 var start = _pos; | 349 var start = _scanner.position; |
397 var end; | 350 var end; |
398 transaction(() { | 351 transaction(() { |
399 fn(); | 352 fn(); |
400 end = _pos; | 353 end = _scanner.position; |
401 return false; | 354 return false; |
402 }); | 355 }); |
403 _errorAnnotations[new _Range(start, end)] = message; | 356 _errorAnnotations[new _Range(start, end)] = message; |
404 } | 357 } |
405 | 358 |
406 /// Throws an error with additional context information. | 359 /// Throws an error with additional context information. |
407 error(String message) { | 360 void error(String message) => |
408 // Line and column should be one-based. | 361 _scanner.error("$message (in $_farthestContext)."); |
409 throw new SyntaxError(_line + 1, _column + 1, | |
410 "$message (in $_farthestContext)."); | |
411 } | |
412 | 362 |
413 /// If [result] is falsey, throws an error saying that [expected] was | 363 /// If [result] is falsey, throws an error saying that [expected] was |
414 /// expected. | 364 /// expected. |
415 expect(result, String expected) { | 365 expect(result, String expected) { |
416 if (truth(result)) return result; | 366 if (truth(result)) return result; |
417 error("Expected $expected"); | 367 error("Expected $expected"); |
418 } | 368 } |
419 | 369 |
420 /// Throws an error saying that the parse failed. Uses [_farthestLine], | 370 /// Throws an error saying that the parse failed. |
421 /// [_farthestColumn], and [_farthestContext] to provide additional | 371 /// |
| 372 /// Uses [_farthestState] and [_farthestContext] to provide additional |
422 /// information. | 373 /// information. |
423 parseFailed() { | 374 parseFailed() { |
424 var message = "Invalid YAML in $_farthestContext"; | 375 var message = "Invalid YAML in $_farthestContext"; |
425 var extraError = _errorAnnotations[_farthestPos]; | 376 _refreshFarthestState(); |
| 377 _scanner.state = _farthestState; |
| 378 |
| 379 var extraError = _errorAnnotations[_scanner.position]; |
426 if (extraError != null) message = "$message ($extraError)"; | 380 if (extraError != null) message = "$message ($extraError)"; |
427 throw new SyntaxError(_farthestLine + 1, _farthestColumn + 1, "$message."); | 381 _scanner.error("$message."); |
| 382 } |
| 383 |
| 384 /// Update [_farthestState] if the scanner is farther than it's been before. |
| 385 void _refreshFarthestState() { |
| 386 if (_scanner.position <= _farthestState.position) return; |
| 387 _farthestState = _scanner.state; |
428 } | 388 } |
429 | 389 |
430 /// Returns the number of spaces after the current position. | 390 /// Returns the number of spaces after the current position. |
431 int countIndentation() { | 391 int countIndentation() { |
432 var i = 0; | 392 var i = 0; |
433 while (peek(i) == SP) i++; | 393 while (peek(i) == SP) i++; |
434 return i; | 394 return i; |
435 } | 395 } |
436 | 396 |
437 /// Returns the indentation for a block scalar. | 397 /// Returns the indentation for a block scalar. |
438 int blockScalarAdditionalIndentation(_BlockHeader header, int indent) { | 398 int blockScalarAdditionalIndentation(_BlockHeader header, int indent) { |
439 if (!header.autoDetectIndent) return header.additionalIndent; | 399 if (!header.autoDetectIndent) return header.additionalIndent; |
440 | 400 |
441 var maxSpaces = 0; | 401 var maxSpaces = 0; |
442 var maxSpacesLine = 0; | |
443 var spaces = 0; | 402 var spaces = 0; |
444 transaction(() { | 403 transaction(() { |
445 do { | 404 do { |
446 spaces = captureString(() => zeroOrMore(() => consumeChar(SP))).length; | 405 spaces = captureString(() => zeroOrMore(() => consumeChar(SP))).length; |
447 if (spaces > maxSpaces) { | 406 if (spaces > maxSpaces) maxSpaces = spaces; |
448 maxSpaces = spaces; | |
449 maxSpacesLine = _line; | |
450 } | |
451 } while (b_break()); | 407 } while (b_break()); |
452 return false; | 408 return false; |
453 }); | 409 }); |
454 | 410 |
455 // If the next non-empty line isn't indented further than the start of the | 411 // If the next non-empty line isn't indented further than the start of the |
456 // block scalar, that means the scalar is going to be empty. Returning any | 412 // block scalar, that means the scalar is going to be empty. Returning any |
457 // value > 0 will cause the parser not to consume any text. | 413 // value > 0 will cause the parser not to consume any text. |
458 if (spaces <= indent) return 1; | 414 if (spaces <= indent) return 1; |
459 | 415 |
460 // It's an error for a leading empty line to be indented more than the first | 416 // It's an error for a leading empty line to be indented more than the first |
461 // non-empty line. | 417 // non-empty line. |
462 if (maxSpaces > spaces) { | 418 if (maxSpaces > spaces) { |
463 throw new SyntaxError(maxSpacesLine + 1, maxSpaces, | 419 _scanner.error("Leading empty lines may not be indented more than the " |
464 "Leading empty lines may not be indented more than the first " | 420 "first non-empty line."); |
465 "non-empty line."); | |
466 } | 421 } |
467 | 422 |
468 return spaces - indent; | 423 return spaces - indent; |
469 } | 424 } |
470 | 425 |
471 /// Returns whether the current position is at the beginning of a line. | 426 /// Returns whether the current position is at the beginning of a line. |
472 bool get atStartOfLine => _column == 0; | 427 bool get atStartOfLine => _scanner.column == 0; |
473 | |
474 /// Returns whether the current position is at the end of the input. | |
475 bool get atEndOfFile => _pos == _len; | |
476 | 428 |
477 /// Given an indicator character, returns the type of that indicator (or null | 429 /// Given an indicator character, returns the type of that indicator (or null |
478 /// if the indicator isn't found. | 430 /// if the indicator isn't found. |
479 int indicatorType(int char) { | 431 int indicatorType(int char) { |
480 switch (char) { | 432 switch (char) { |
481 case HYPHEN: return C_SEQUENCE_ENTRY; | 433 case HYPHEN: return C_SEQUENCE_ENTRY; |
482 case QUESTION_MARK: return C_MAPPING_KEY; | 434 case QUESTION_MARK: return C_MAPPING_KEY; |
483 case COLON: return C_MAPPING_VALUE; | 435 case COLON: return C_MAPPING_VALUE; |
484 case COMMA: return C_COLLECT_ENTRY; | 436 case COMMA: return C_COLLECT_ENTRY; |
485 case LEFT_BRACKET: return C_SEQUENCE_START; | 437 case LEFT_BRACKET: return C_SEQUENCE_START; |
(...skipping 11 matching lines...) Expand all Loading... |
497 case PERCENT: return C_DIRECTIVE; | 449 case PERCENT: return C_DIRECTIVE; |
498 case AT: | 450 case AT: |
499 case GRAVE_ACCENT: | 451 case GRAVE_ACCENT: |
500 return C_RESERVED; | 452 return C_RESERVED; |
501 default: return null; | 453 default: return null; |
502 } | 454 } |
503 } | 455 } |
504 | 456 |
505 // 1 | 457 // 1 |
506 bool isPrintable(int char) { | 458 bool isPrintable(int char) { |
| 459 if (char == null) return false; |
507 return char == TAB || | 460 return char == TAB || |
508 char == LF || | 461 char == LF || |
509 char == CR || | 462 char == CR || |
510 (char >= SP && char <= TILDE) || | 463 (char >= SP && char <= TILDE) || |
511 char == NEL || | 464 char == NEL || |
512 (char >= 0xA0 && char <= 0xD7FF) || | 465 (char >= 0xA0 && char <= 0xD7FF) || |
513 (char >= 0xE000 && char <= 0xFFFD) || | 466 (char >= 0xE000 && char <= 0xFFFD) || |
514 (char >= 0x10000 && char <= 0x10FFFF); | 467 (char >= 0x10000 && char <= 0x10FFFF); |
515 } | 468 } |
516 | 469 |
517 // 2 | 470 // 2 |
518 bool isJson(int char) => char == TAB || (char >= SP && char <= 0x10FFFF); | 471 bool isJson(int char) => char != null && |
| 472 (char == TAB || (char >= SP && char <= 0x10FFFF)); |
519 | 473 |
520 // 22 | 474 // 22 |
521 bool c_indicator(int type) => consume((c) => indicatorType(c) == type); | 475 bool c_indicator(int type) => consume((c) => indicatorType(c) == type); |
522 | 476 |
523 // 23 | 477 // 23 |
524 bool isFlowIndicator(int char) { | 478 bool isFlowIndicator(int char) { |
525 var indicator = indicatorType(char); | 479 var indicator = indicatorType(char); |
526 return indicator == C_COLLECT_ENTRY || | 480 return indicator == C_COLLECT_ENTRY || |
527 indicator == C_SEQUENCE_START || | 481 indicator == C_SEQUENCE_START || |
528 indicator == C_SEQUENCE_END || | 482 indicator == C_SEQUENCE_END || |
(...skipping 22 matching lines...) Expand all Loading... |
551 // 30 | 505 // 30 |
552 bool b_nonContent() => captureAs("", () => b_break()); | 506 bool b_nonContent() => captureAs("", () => b_break()); |
553 | 507 |
554 // 33 | 508 // 33 |
555 bool isSpace(int char) => char == SP || char == TAB; | 509 bool isSpace(int char) => char == SP || char == TAB; |
556 | 510 |
557 // 34 | 511 // 34 |
558 bool isNonSpace(int char) => isNonBreak(char) && !isSpace(char); | 512 bool isNonSpace(int char) => isNonBreak(char) && !isSpace(char); |
559 | 513 |
560 // 35 | 514 // 35 |
561 bool isDecDigit(int char) => char >= NUMBER_0 && char <= NUMBER_9; | 515 bool isDecDigit(int char) => char != null && char >= NUMBER_0 && |
| 516 char <= NUMBER_9; |
562 | 517 |
563 // 36 | 518 // 36 |
564 bool isHexDigit(int char) { | 519 bool isHexDigit(int char) { |
| 520 if (char == null) return false; |
565 return isDecDigit(char) || | 521 return isDecDigit(char) || |
566 (char >= LETTER_A && char <= LETTER_F) || | 522 (char >= LETTER_A && char <= LETTER_F) || |
567 (char >= LETTER_CAP_A && char <= LETTER_CAP_F); | 523 (char >= LETTER_CAP_A && char <= LETTER_CAP_F); |
568 } | 524 } |
569 | 525 |
570 // 41 | 526 // 41 |
571 bool c_escape() => captureAs("", () => consumeChar(BACKSLASH)); | 527 bool c_escape() => captureAs("", () => consumeChar(BACKSLASH)); |
572 | 528 |
573 // 42 | 529 // 42 |
574 bool ns_escNull() => captureAs("\x00", () => consumeChar(NUMBER_0)); | 530 bool ns_escNull() => captureAs("\x00", () => consumeChar(NUMBER_0)); |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
747 }); | 703 }); |
748 | 704 |
749 // 75 | 705 // 75 |
750 bool c_nb_commentText() { | 706 bool c_nb_commentText() { |
751 if (!truth(c_indicator(C_COMMENT))) return false; | 707 if (!truth(c_indicator(C_COMMENT))) return false; |
752 zeroOrMore(() => consume(isNonBreak)); | 708 zeroOrMore(() => consume(isNonBreak)); |
753 return true; | 709 return true; |
754 } | 710 } |
755 | 711 |
756 // 76 | 712 // 76 |
757 bool b_comment() => atEndOfFile || b_nonContent(); | 713 bool b_comment() => _scanner.isDone || b_nonContent(); |
758 | 714 |
759 // 77 | 715 // 77 |
760 bool s_b_comment() { | 716 bool s_b_comment() { |
761 if (truth(s_separateInLine())) { | 717 if (truth(s_separateInLine())) { |
762 zeroOrOne(c_nb_commentText); | 718 zeroOrOne(c_nb_commentText); |
763 } | 719 } |
764 return b_comment(); | 720 return b_comment(); |
765 } | 721 } |
766 | 722 |
767 // 78 | 723 // 78 |
(...skipping 653 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1421 case PLUS: | 1377 case PLUS: |
1422 next(); | 1378 next(); |
1423 return CHOMPING_KEEP; | 1379 return CHOMPING_KEEP; |
1424 default: | 1380 default: |
1425 return CHOMPING_CLIP; | 1381 return CHOMPING_CLIP; |
1426 } | 1382 } |
1427 } | 1383 } |
1428 | 1384 |
1429 // 165 | 1385 // 165 |
1430 bool b_chompedLast(int chomping) { | 1386 bool b_chompedLast(int chomping) { |
1431 if (atEndOfFile) return true; | 1387 if (_scanner.isDone) return true; |
1432 switch (chomping) { | 1388 switch (chomping) { |
1433 case CHOMPING_STRIP: | 1389 case CHOMPING_STRIP: |
1434 return b_nonContent(); | 1390 return b_nonContent(); |
1435 case CHOMPING_CLIP: | 1391 case CHOMPING_CLIP: |
1436 case CHOMPING_KEEP: | 1392 case CHOMPING_KEEP: |
1437 return b_asLineFeed(); | 1393 return b_asLineFeed(); |
1438 } | 1394 } |
1439 throw "unreachable"; | 1395 throw "unreachable"; |
1440 } | 1396 } |
1441 | 1397 |
(...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1803 return s_l_comments(); | 1759 return s_l_comments(); |
1804 }); | 1760 }); |
1805 | 1761 |
1806 // 206 | 1762 // 206 |
1807 bool c_forbidden() { | 1763 bool c_forbidden() { |
1808 if (!_inBareDocument || !atStartOfLine) return false; | 1764 if (!_inBareDocument || !atStartOfLine) return false; |
1809 var forbidden = false; | 1765 var forbidden = false; |
1810 transaction(() { | 1766 transaction(() { |
1811 if (!truth(or([c_directivesEnd, c_documentEnd]))) return; | 1767 if (!truth(or([c_directivesEnd, c_documentEnd]))) return; |
1812 var char = peek(); | 1768 var char = peek(); |
1813 forbidden = isBreak(char) || isSpace(char) || atEndOfFile; | 1769 forbidden = isBreak(char) || isSpace(char) || _scanner.isDone; |
1814 return; | 1770 return; |
1815 }); | 1771 }); |
1816 return forbidden; | 1772 return forbidden; |
1817 } | 1773 } |
1818 | 1774 |
1819 // 207 | 1775 // 207 |
1820 Node l_bareDocument() { | 1776 Node l_bareDocument() { |
1821 try { | 1777 try { |
1822 _inBareDocument = true; | 1778 _inBareDocument = true; |
1823 return s_l_blockNode(-1, BLOCK_IN); | 1779 return s_l_blockNode(-1, BLOCK_IN); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1864 zeroOrMore(l_documentPrefix); | 1820 zeroOrMore(l_documentPrefix); |
1865 doc = zeroOrOne(l_anyDocument); | 1821 doc = zeroOrOne(l_anyDocument); |
1866 } else { | 1822 } else { |
1867 zeroOrMore(l_documentPrefix); | 1823 zeroOrMore(l_documentPrefix); |
1868 doc = zeroOrOne(l_explicitDocument); | 1824 doc = zeroOrOne(l_explicitDocument); |
1869 } | 1825 } |
1870 if (truth(doc)) docs.add(doc); | 1826 if (truth(doc)) docs.add(doc); |
1871 return doc; | 1827 return doc; |
1872 }); | 1828 }); |
1873 | 1829 |
1874 if (!atEndOfFile) parseFailed(); | 1830 if (!_scanner.isDone) parseFailed(); |
1875 return docs; | 1831 return docs; |
1876 } | 1832 } |
1877 } | 1833 } |
1878 | 1834 |
1879 class SyntaxError extends YamlException { | |
1880 final int _line; | |
1881 final int _column; | |
1882 | |
1883 SyntaxError(this._line, this._column, String msg) : super(msg); | |
1884 | |
1885 String toString() => "Syntax error on line $_line, column $_column: " | |
1886 "${super.toString()}"; | |
1887 } | |
1888 | |
1889 /// A pair of values. | 1835 /// A pair of values. |
1890 class _Pair<E, F> { | 1836 class _Pair<E, F> { |
1891 E first; | 1837 E first; |
1892 F last; | 1838 F last; |
1893 | 1839 |
1894 _Pair(this.first, this.last); | 1840 _Pair(this.first, this.last); |
1895 | 1841 |
1896 String toString() => '($first, $last)'; | 1842 String toString() => '($first, $last)'; |
1897 } | 1843 } |
1898 | 1844 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1939 for (var pair in _contents.reversed) { | 1885 for (var pair in _contents.reversed) { |
1940 if (pair.first.contains(pos)) return pair.last; | 1886 if (pair.first.contains(pos)) return pair.last; |
1941 } | 1887 } |
1942 return null; | 1888 return null; |
1943 } | 1889 } |
1944 | 1890 |
1945 /// Associates [value] with [range]. | 1891 /// Associates [value] with [range]. |
1946 operator[]=(_Range range, E value) => | 1892 operator[]=(_Range range, E value) => |
1947 _contents.add(new _Pair<_Range, E>(range, value)); | 1893 _contents.add(new _Pair<_Range, E>(range, value)); |
1948 } | 1894 } |
OLD | NEW |