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