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