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 |