| 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 |