Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Side by Side Diff: pkg/yaml/lib/src/parser.dart

Issue 689513002: Rewrite the pkg/yaml parser. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Update string_scanner dependency. Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library yaml.parser; 5 library yaml.parser;
6 6
7 import 'dart:collection';
8
9 import 'package:source_span/source_span.dart'; 7 import 'package:source_span/source_span.dart';
10 import 'package:string_scanner/string_scanner.dart'; 8 import 'package:string_scanner/string_scanner.dart';
11 9
12 import 'equality.dart'; 10 import 'event.dart';
13 import 'model.dart'; 11 import 'scanner.dart';
12 import 'style.dart';
13 import 'token.dart';
14 import 'utils.dart'; 14 import 'utils.dart';
15 15 import 'yaml_document.dart';
16 /// Translates a string of characters into a YAML serialization tree. 16 import 'yaml_exception.dart';
17
18 /// A parser that reads [Token]s emitted by a [Scanner] and emits [Event]s.
17 /// 19 ///
18 /// This parser is designed to closely follow the spec. All productions in the 20 /// This is based on the libyaml parser, available at
19 /// spec are numbered, and the corresponding methods in the parser have the same 21 /// https://github.com/yaml/libyaml/blob/master/src/parser.c. The license for
20 /// numbers. This is certainly not the most efficient way of parsing YAML, but 22 /// that is available in ../../libyaml-license.txt.
21 /// it is the easiest to write and read in the context of the spec.
22 ///
23 /// Methods corresponding to productions are also named as in the spec,
24 /// translating the name of the method (although not the annotation characters)
25 /// into camel-case for dart style.. For example, the spec has a production
26 /// named `nb-ns-plain-in-line`, and the method implementing it is named
27 /// `nb_ns_plainInLine`. The exception to that rule is methods that just
28 /// recognize character classes; these are named `is*`.
29 class Parser { 23 class Parser {
30 static const TAB = 0x9; 24 /// The underlying [Scanner] that generates [Token]s.
31 static const LF = 0xA; 25 final Scanner _scanner;
32 static const CR = 0xD; 26
33 static const SP = 0x20; 27 /// The stack of parse states for nested contexts.
34 static const TILDE = 0x7E; 28 final _states = new List<_State>();
35 static const NEL = 0x85; 29
36 static const PLUS = 0x2B; 30 /// The current parse state.
37 static const HYPHEN = 0x2D; 31 var _state = _State.STREAM_START;
38 static const QUESTION_MARK = 0x3F; 32
39 static const COLON = 0x3A; 33 /// The custom tag directives, by tag handle.
40 static const COMMA = 0x2C; 34 final _tagDirectives = new Map<String, TagDirective>();
41 static const LEFT_BRACKET = 0x5B; 35
42 static const RIGHT_BRACKET = 0x5D; 36 /// Whether the parser has finished parsing.
43 static const LEFT_BRACE = 0x7B; 37 bool get isDone => _state == _State.END;
44 static const RIGHT_BRACE = 0x7D; 38
45 static const HASH = 0x23; 39 /// Creates a parser that parses [source].
46 static const AMPERSAND = 0x26; 40 ///
47 static const ASTERISK = 0x2A; 41 /// [sourceUrl] can be a String or a [Uri].
48 static const EXCLAMATION = 0x21; 42 Parser(String source, {sourceUrl})
49 static const VERTICAL_BAR = 0x7C; 43 : _scanner = new Scanner(source, sourceUrl: sourceUrl);
50 static const GREATER_THAN = 0x3E; 44
51 static const SINGLE_QUOTE = 0x27; 45 /// Consumes and returns the next event.
52 static const DOUBLE_QUOTE = 0x22; 46 Event parse() {
53 static const PERCENT = 0x25;
54 static const AT = 0x40;
55 static const GRAVE_ACCENT = 0x60;
56
57 static const NULL = 0x0;
58 static const BELL = 0x7;
59 static const BACKSPACE = 0x8;
60 static const VERTICAL_TAB = 0xB;
61 static const FORM_FEED = 0xC;
62 static const ESCAPE = 0x1B;
63 static const SLASH = 0x2F;
64 static const BACKSLASH = 0x5C;
65 static const UNDERSCORE = 0x5F;
66 static const NBSP = 0xA0;
67 static const LINE_SEPARATOR = 0x2028;
68 static const PARAGRAPH_SEPARATOR = 0x2029;
69
70 static const NUMBER_0 = 0x30;
71 static const NUMBER_9 = 0x39;
72
73 static const LETTER_A = 0x61;
74 static const LETTER_B = 0x62;
75 static const LETTER_E = 0x65;
76 static const LETTER_F = 0x66;
77 static const LETTER_N = 0x6E;
78 static const LETTER_R = 0x72;
79 static const LETTER_T = 0x74;
80 static const LETTER_U = 0x75;
81 static const LETTER_V = 0x76;
82 static const LETTER_X = 0x78;
83
84 static const LETTER_CAP_A = 0x41;
85 static const LETTER_CAP_F = 0x46;
86 static const LETTER_CAP_L = 0x4C;
87 static const LETTER_CAP_N = 0x4E;
88 static const LETTER_CAP_P = 0x50;
89 static const LETTER_CAP_U = 0x55;
90 static const LETTER_CAP_X = 0x58;
91
92 static const C_SEQUENCE_ENTRY = 4;
93 static const C_MAPPING_KEY = 5;
94 static const C_MAPPING_VALUE = 6;
95 static const C_COLLECT_ENTRY = 7;
96 static const C_SEQUENCE_START = 8;
97 static const C_SEQUENCE_END = 9;
98 static const C_MAPPING_START = 10;
99 static const C_MAPPING_END = 11;
100 static const C_COMMENT = 12;
101 static const C_ANCHOR = 13;
102 static const C_ALIAS = 14;
103 static const C_TAG = 15;
104 static const C_LITERAL = 16;
105 static const C_FOLDED = 17;
106 static const C_SINGLE_QUOTE = 18;
107 static const C_DOUBLE_QUOTE = 19;
108 static const C_DIRECTIVE = 20;
109 static const C_RESERVED = 21;
110
111 static const BLOCK_OUT = 0;
112 static const BLOCK_IN = 1;
113 static const FLOW_OUT = 2;
114 static const FLOW_IN = 3;
115 static const BLOCK_KEY = 4;
116 static const FLOW_KEY = 5;
117
118 static const CHOMPING_STRIP = 0;
119 static const CHOMPING_KEEP = 1;
120 static const CHOMPING_CLIP = 2;
121
122 /// The scanner that's used to scan through the document.
123 final SpanScanner _scanner;
124
125 /// Whether we're parsing a bare document (that is, one that doesn't begin
126 /// with `---`). Bare documents don't allow `%` immediately following
127 /// newlines.
128 bool _inBareDocument = false;
129
130 /// The state of the scanner when it was the farthest in the document it's
131 /// been.
132 LineScannerState _farthestState;
133
134 /// The name of the context of the farthest position that has been parsed
135 /// successfully before backtracking. Used for error reporting.
136 String _farthestContext = "document";
137
138 /// A stack of the names of parse contexts. Used for error reporting.
139 final _contextStack = <String>["document"];
140
141 /// Annotations attached to ranges of the source string that add extra
142 /// information to any errors that occur in the annotated range.
143 final _errorAnnotations = new _RangeMap<String>();
144
145 /// The buffer containing the string currently being captured.
146 StringBuffer _capturedString;
147
148 /// The beginning of the current section of the captured string.
149 int _captureStart;
150
151 /// Whether the current string capture is being overridden.
152 bool _capturingAs = false;
153
154 Parser(String yaml, sourceUrl)
155 : _scanner = new SpanScanner(yaml, sourceUrl: sourceUrl) {
156 _farthestState = _scanner.state;
157 }
158
159 /// Returns the character at the current position, then moves that position
160 /// forward one character.
161 int next() => _scanner.readChar();
162
163 /// Returns the code unit at the current position, or the character [i]
164 /// characters after the current position.
165 int peek([int i = 0]) => _scanner.peekChar(i);
166
167 /// The truthiness operator. Returns `false` if [obj] is `null` or `false`,
168 /// `true` otherwise.
169 bool truth(obj) => obj != null && obj != false;
170
171 /// Consumes the current character if it matches [matcher]. Returns the result
172 /// of [matcher].
173 bool consume(bool matcher(int)) {
174 if (matcher(peek())) {
175 next();
176 return true;
177 }
178 return false;
179 }
180
181 /// Consumes the current character if it equals [char].
182 bool consumeChar(int char) => consume((c) => c == char);
183
184 /// Calls [consumer] until it returns a falsey value. Returns a list of all
185 /// truthy return values of [consumer], or null if it didn't consume anything.
186 ///
187 /// Conceptually, repeats a production one or more times.
188 List oneOrMore(consumer()) {
189 var first = consumer();
190 if (!truth(first)) return null;
191 var out = [first];
192 while (true) {
193 var el = consumer();
194 if (!truth(el)) return out;
195 out.add(el);
196 }
197 return null; // Unreachable.
198 }
199
200 /// Calls [consumer] until it returns a falsey value. Returns a list of all
201 /// truthy return values of [consumer], or the empty list if it didn't consume
202 /// anything.
203 ///
204 /// Conceptually, repeats a production any number of times.
205 List zeroOrMore(consumer()) {
206 var out = [];
207 var oldPos = _scanner.position;
208 while (true) {
209 var el = consumer();
210 if (!truth(el) || oldPos == _scanner.position) return out;
211 oldPos = _scanner.position;
212 out.add(el);
213 }
214 return null; // Unreachable.
215 }
216
217 /// Just calls [consumer] and returns its result. Used to make it explicit
218 /// that a production is intended to be optional.
219 zeroOrOne(consumer()) => consumer();
220
221 /// Calls each function in [consumers] until one returns a truthy value, then
222 /// returns that.
223 or(List<Function> consumers) {
224 for (var c in consumers) {
225 var res = c();
226 if (truth(res)) return res;
227 }
228 return null;
229 }
230
231 /// Calls [consumer] and returns its result, but rolls back the parser state
232 /// if [consumer] returns a falsey value.
233 transaction(consumer()) {
234 var oldState = _scanner.state;
235 var oldCaptureStart = _captureStart;
236 String capturedSoFar = _capturedString == null ? null :
237 _capturedString.toString();
238 var res = consumer();
239 _refreshFarthestState();
240 if (truth(res)) return res;
241
242 _scanner.state = oldState;
243 _captureStart = oldCaptureStart;
244 _capturedString = capturedSoFar == null ? null :
245 new StringBuffer(capturedSoFar);
246 return res;
247 }
248
249 /// Consumes [n] characters matching [matcher], or none if there isn't a
250 /// complete match. The first argument to [matcher] is the character code, the
251 /// second is the index (from 0 to [n] - 1).
252 ///
253 /// Returns whether or not the characters were consumed.
254 bool nAtOnce(int n, bool matcher(int c, int i)) => transaction(() {
255 for (int i = 0; i < n; i++) {
256 if (!consume((c) => matcher(c, i))) return false;
257 }
258 return true;
259 });
260
261 /// Consumes the exact characters in [str], or nothing.
262 ///
263 /// Returns whether or not the string was consumed.
264 bool rawString(String str) =>
265 nAtOnce(str.length, (c, i) => str.codeUnitAt(i) == c);
266
267 /// Consumes and returns a string of characters matching [matcher], or null if
268 /// there are no such characters.
269 String stringOf(bool matcher(int)) =>
270 captureString(() => oneOrMore(() => consume(matcher)));
271
272 /// Calls [consumer] and returns the string that was consumed while doing so,
273 /// or null if [consumer] returned a falsey value. Automatically wraps
274 /// [consumer] in `transaction`.
275 String captureString(consumer()) {
276 // captureString calls may not be nested
277 assert(_capturedString == null);
278
279 _captureStart = _scanner.position;
280 _capturedString = new StringBuffer();
281 var res = transaction(consumer);
282 if (!truth(res)) {
283 _captureStart = null;
284 _capturedString = null;
285 return null;
286 }
287
288 flushCapture();
289 var result = _capturedString.toString();
290 _captureStart = null;
291 _capturedString = null;
292 return result;
293 }
294
295 captureAs(String replacement, consumer()) =>
296 captureAndTransform(consumer, (_) => replacement);
297
298 captureAndTransform(consumer(), String transformation(String captured)) {
299 if (_capturedString == null) return consumer();
300 if (_capturingAs) return consumer();
301
302 flushCapture();
303 _capturingAs = true;
304 var res = consumer();
305 _capturingAs = false;
306 if (!truth(res)) return res;
307
308 _capturedString.write(transformation(
309 _scanner.string.substring(_captureStart, _scanner.position)));
310 _captureStart = _scanner.position;
311 return res;
312 }
313
314 void flushCapture() {
315 _capturedString.write(_scanner.string.substring(
316 _captureStart, _scanner.position));
317 _captureStart = _scanner.position;
318 }
319
320 /// Adds a tag and an anchor to [node], if they're defined.
321 Node addProps(Node node, Pair<Tag, String> props) {
322 if (props == null || node == null) return node;
323 if (truth(props.first)) node.tag = props.first;
324 if (truth(props.last)) node.anchor = props.last;
325 return node;
326 }
327
328 /// Creates a MappingNode from [pairs].
329 MappingNode map(List<Pair<Node, Node>> pairs, SourceSpan span) {
330 var content = new Map<Node, Node>();
331 pairs.forEach((pair) => content[pair.first] = pair.last);
332 return new MappingNode("?", content, span);
333 }
334
335 /// Runs [fn] in a context named [name]. Used for error reporting.
336 context(String name, fn()) {
337 try { 47 try {
338 _contextStack.add(name); 48 if (isDone) throw new StateError("No more events.");
339 return fn(); 49 var event = _stateMachine();
340 } finally { 50 return event;
341 var popped = _contextStack.removeLast(); 51 } on StringScannerException catch (error) {
342 assert(popped == name); 52 throw new YamlException(error.message, error.span);
343 } 53 }
344 } 54 }
345 55
346 /// Adds [message] as extra information to any errors that occur between the 56 /// Dispatches parsing based on the current state.
347 /// current position and the position of the cursor after running [fn]. The 57 Event _stateMachine() {
348 /// cursor is reset after [fn] is run. 58 switch (_state) {
349 annotateError(String message, fn()) { 59 case _State.STREAM_START:
350 var start = _scanner.position; 60 return _parseStreamStart();
351 var end; 61 case _State.DOCUMENT_START:
352 transaction(() { 62 return _parseDocumentStart();
353 fn(); 63 case _State.DOCUMENT_CONTENT:
354 end = _scanner.position; 64 return _parseDocumentContent();
355 return false; 65 case _State.DOCUMENT_END:
356 }); 66 return _parseDocumentEnd();
357 _errorAnnotations[new _Range(start, end)] = message; 67 case _State.BLOCK_NODE:
358 } 68 return _parseNode(block: true);
359 69 case _State.BLOCK_NODE_OR_INDENTLESS_SEQUENCE:
360 /// Throws an error with additional context information. 70 return _parseNode(block: true, indentlessSequence: true);
361 void error(String message) => 71 case _State.FLOW_NODE:
362 _scanner.error("$message (in $_farthestContext)."); 72 return _parseNode();
363 73 case _State.BLOCK_SEQUENCE_FIRST_ENTRY:
Bob Nystrom 2014/10/31 20:03:27 How about just putting _scanner.scan() here and re
nweiz 2014/11/04 22:19:36 Done.
364 /// If [result] is falsey, throws an error saying that [expected] was 74 return _parseBlockSequenceEntry(first: true);
365 /// expected. 75 case _State.BLOCK_SEQUENCE_ENTRY:
366 expect(result, String expected) { 76 return _parseBlockSequenceEntry();
367 if (truth(result)) return result; 77 case _State.INDENTLESS_SEQUENCE_ENTRY:
368 error("Expected $expected"); 78 return _parseIndentlessSequenceEntry();
369 } 79 case _State.BLOCK_MAPPING_FIRST_KEY:
370 80 return _parseBlockMappingKey(first: true);
371 /// Throws an error saying that the parse failed. 81 case _State.BLOCK_MAPPING_KEY:
372 /// 82 return _parseBlockMappingKey();
373 /// Uses [_farthestState] and [_farthestContext] to provide additional 83 case _State.BLOCK_MAPPING_VALUE:
374 /// information. 84 return _parseBlockMappingValue();
375 parseFailed() { 85 case _State.FLOW_SEQUENCE_FIRST_ENTRY:
376 var message = "Invalid YAML in $_farthestContext"; 86 return _parseFlowSequenceEntry(first: true);
377 _refreshFarthestState(); 87 case _State.FLOW_SEQUENCE_ENTRY:
378 _scanner.state = _farthestState; 88 return _parseFlowSequenceEntry();
379 89 case _State.FLOW_SEQUENCE_ENTRY_MAPPING_KEY:
380 var extraError = _errorAnnotations[_scanner.position]; 90 return _parseFlowSequenceEntryMappingKey();
381 if (extraError != null) message = "$message ($extraError)"; 91 case _State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE:
382 _scanner.error("$message."); 92 return _parseFlowSequenceEntryMappingValue();
383 } 93 case _State.FLOW_SEQUENCE_ENTRY_MAPPING_END:
384 94 return _parseFlowSequenceEntryMappingEnd();
385 /// Update [_farthestState] if the scanner is farther than it's been before. 95 case _State.FLOW_MAPPING_FIRST_KEY:
386 void _refreshFarthestState() { 96 return _parseFlowMappingKey(first: true);
387 if (_scanner.position <= _farthestState.position) return; 97 case _State.FLOW_MAPPING_KEY:
388 _farthestState = _scanner.state; 98 return _parseFlowMappingKey();
389 } 99 case _State.FLOW_MAPPING_VALUE:
390 100 return _parseFlowMappingValue();
391 /// Returns the number of spaces after the current position. 101 case _State.FLOW_MAPPING_EMPTY_VALUE:
392 int countIndentation() { 102 return _parseFlowMappingValue(empty: true);
393 var i = 0; 103 default:
394 while (peek(i) == SP) i++; 104 throw "Unreachable";
395 return i; 105 }
396 } 106 }
397 107
398 /// Returns the indentation for a block scalar. 108 /// Parses the production:
399 int blockScalarAdditionalIndentation(_BlockHeader header, int indent) { 109 ///
400 if (!header.autoDetectIndent) return header.additionalIndent; 110 /// stream ::=
401 111 /// STREAM-START implicit_document? explicit_document* STREAM-END
402 var maxSpaces = 0; 112 /// ************
403 var spaces = 0; 113 Event _parseStreamStart() {
404 transaction(() { 114 var token = _scanner.scan();
405 do { 115 assert(token.type == TokenType.STREAM_START);
406 spaces = captureString(() => zeroOrMore(() => consumeChar(SP))).length; 116
407 if (spaces > maxSpaces) maxSpaces = spaces; 117 _state = _State.DOCUMENT_START;
408 } while (b_break()); 118 return new Event(EventType.STREAM_START, token.span);
409 return false; 119 }
410 }); 120
411 121 /// Parses the productions:
412 // If the next non-empty line isn't indented further than the start of the 122 ///
413 // block scalar, that means the scalar is going to be empty. Returning any 123 /// implicit_document ::= block_node DOCUMENT-END*
414 // value > 0 will cause the parser not to consume any text. 124 /// *
415 if (spaces <= indent) return 1; 125 /// explicit_document ::=
416 126 /// DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
417 // It's an error for a leading empty line to be indented more than the first 127 /// *************************
418 // non-empty line. 128 Event _parseDocumentStart() {
419 if (maxSpaces > spaces) { 129 var token = _scanner.peek();
420 _scanner.error("Leading empty lines may not be indented more than the " 130
421 "first non-empty line."); 131 // libyaml requires any document beyond the first in the stream to have an
422 } 132 // explicit document start indicator, but the spec allows it to be omitted
423 133 // as long as there was an end indicator.
424 return spaces - indent; 134
425 } 135 // Parse extra document end indicators.
426 136 while (token.type == TokenType.DOCUMENT_END) {
427 /// Returns whether the current position is at the beginning of a line. 137 _scanner.scan();
428 bool get atStartOfLine => _scanner.column == 0; 138 token = _scanner.peek();
429 139 }
430 /// Given an indicator character, returns the type of that indicator (or null 140
431 /// if the indicator isn't found. 141 if (token.type != TokenType.VERSION_DIRECTIVE &&
432 int indicatorType(int char) { 142 token.type != TokenType.TAG_DIRECTIVE &&
433 switch (char) { 143 token.type != TokenType.DOCUMENT_START &&
434 case HYPHEN: return C_SEQUENCE_ENTRY; 144 token.type != TokenType.STREAM_END) {
435 case QUESTION_MARK: return C_MAPPING_KEY; 145 // Parse an implicit document.
436 case COLON: return C_MAPPING_VALUE; 146 _processDirectives();
437 case COMMA: return C_COLLECT_ENTRY; 147 _states.add(_State.DOCUMENT_END);
438 case LEFT_BRACKET: return C_SEQUENCE_START; 148 _state = _State.BLOCK_NODE;
439 case RIGHT_BRACKET: return C_SEQUENCE_END; 149 return new DocumentStartEvent(token.span.start.pointSpan());
440 case LEFT_BRACE: return C_MAPPING_START; 150 }
441 case RIGHT_BRACE: return C_MAPPING_END; 151
442 case HASH: return C_COMMENT; 152 if (token.type == TokenType.STREAM_END) {
443 case AMPERSAND: return C_ANCHOR; 153 _state = _State.END;
444 case ASTERISK: return C_ALIAS; 154 _scanner.scan();
445 case EXCLAMATION: return C_TAG; 155 return new Event(EventType.STREAM_END, token.span);
446 case VERTICAL_BAR: return C_LITERAL; 156 }
447 case GREATER_THAN: return C_FOLDED; 157
448 case SINGLE_QUOTE: return C_SINGLE_QUOTE; 158 // Parse an explicit document.
449 case DOUBLE_QUOTE: return C_DOUBLE_QUOTE; 159 var start = token.span;
450 case PERCENT: return C_DIRECTIVE; 160 var pair = _processDirectives();
451 case AT: 161 var versionDirective = pair.first;
452 case GRAVE_ACCENT: 162 var tagDirectives = pair.last;
453 return C_RESERVED; 163 token = _scanner.peek();
454 default: return null; 164 if (token.type != TokenType.DOCUMENT_START) {
455 } 165 throw new YamlException("Expected document start.", token.span);
456 } 166 }
457 167
458 // 1 168 _states.add(_State.DOCUMENT_END);
459 bool isPrintable(int char) { 169 _state = _State.DOCUMENT_CONTENT;
460 if (char == null) return false; 170 _scanner.scan();
461 return char == TAB || 171 return new DocumentStartEvent(start.expand(token.span),
462 char == LF || 172 versionDirective: versionDirective,
463 char == CR || 173 tagDirectives: tagDirectives,
464 (char >= SP && char <= TILDE) || 174 implicit: false);
465 char == NEL || 175 }
466 (char >= 0xA0 && char <= 0xD7FF) || 176
467 (char >= 0xE000 && char <= 0xFFFD) || 177 /// Parses the productions:
468 (char >= 0x10000 && char <= 0x10FFFF); 178 ///
469 } 179 /// explicit_document ::=
470 180 /// DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
471 // 2 181 /// ***********
472 bool isJson(int char) => char != null && 182 Event _parseDocumentContent() {
473 (char == TAB || (char >= SP && char <= 0x10FFFF)); 183 var token = _scanner.peek();
474 184
475 // 22 185 switch (token.type) {
476 bool c_indicator(int type) => consume((c) => indicatorType(c) == type); 186 case TokenType.VERSION_DIRECTIVE:
477 187 case TokenType.TAG_DIRECTIVE:
478 // 23 188 case TokenType.DOCUMENT_START:
479 bool isFlowIndicator(int char) { 189 case TokenType.DOCUMENT_END:
480 var indicator = indicatorType(char); 190 case TokenType.STREAM_END:
481 return indicator == C_COLLECT_ENTRY || 191 _state = _states.removeLast();
482 indicator == C_SEQUENCE_START || 192 return _processEmptyScalar(token.span.start);
483 indicator == C_SEQUENCE_END || 193 default:
484 indicator == C_MAPPING_START || 194 return _parseNode(block: true);
485 indicator == C_MAPPING_END; 195 }
486 } 196 }
487 197
488 // 26 198 /// Parses the productions:
489 bool isBreak(int char) => char == LF || char == CR; 199 ///
490 200 /// implicit_document ::= block_node DOCUMENT-END*
491 // 27 201 /// *************
492 bool isNonBreak(int char) => isPrintable(char) && !isBreak(char); 202 /// explicit_document ::=
493 203 /// DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
494 // 28 204 /// *************
495 bool b_break() { 205 Event _parseDocumentEnd() {
496 if (consumeChar(CR)) { 206 var token = _scanner.peek();
497 zeroOrOne(() => consumeChar(LF)); 207 var span = token.span.start.pointSpan();
498 return true; 208 var implicit = true;
499 } 209
500 return consumeChar(LF); 210 if (token.type == TokenType.DOCUMENT_END) {
501 } 211 span = token.span;
502 212 implicit = false;
503 // 29 213 _scanner.scan();
504 bool b_asLineFeed() => captureAs("\n", () => b_break()); 214 }
505 215
506 // 30 216 _tagDirectives.clear();
507 bool b_nonContent() => captureAs("", () => b_break()); 217 _state = _State.DOCUMENT_START;
508 218 return new DocumentEndEvent(span, implicit: implicit);
Bob Nystrom 2014/10/31 20:03:27 How about just: Event _parseDocumentEnd() { _ta
nweiz 2014/11/04 22:19:36 Done.
509 // 33 219 }
510 bool isSpace(int char) => char == SP || char == TAB; 220
511 221 /// Parses the productions:
512 // 34 222 ///
513 bool isNonSpace(int char) => isNonBreak(char) && !isSpace(char); 223 /// block_node_or_indentless_sequence ::=
514 224 /// ALIAS
515 // 35 225 /// *****
516 bool isDecDigit(int char) => char != null && char >= NUMBER_0 && 226 /// | properties (block_content | indentless_block_sequence)?
517 char <= NUMBER_9; 227 /// ********** *
518 228 /// | block_content | indentless_block_sequence
519 // 36 229 /// *
520 bool isHexDigit(int char) { 230 /// block_node ::= ALIAS
521 if (char == null) return false; 231 /// *****
522 return isDecDigit(char) || 232 /// | properties block_content?
523 (char >= LETTER_A && char <= LETTER_F) || 233 /// ********** *
524 (char >= LETTER_CAP_A && char <= LETTER_CAP_F); 234 /// | block_content
525 } 235 /// *
526 236 /// flow_node ::= ALIAS
527 // 41 237 /// *****
528 bool c_escape() => captureAs("", () => consumeChar(BACKSLASH)); 238 /// | properties flow_content?
529 239 /// ********** *
530 // 42 240 /// | flow_content
531 bool ns_escNull() => captureAs("\x00", () => consumeChar(NUMBER_0)); 241 /// *
532 242 /// properties ::= TAG ANCHOR? | ANCHOR TAG?
533 // 43 243 /// *************************
534 bool ns_escBell() => captureAs("\x07", () => consumeChar(LETTER_A)); 244 /// block_content ::= block_collection | flow_collection | SCALAR
535 245 /// ******
536 // 44 246 /// flow_content ::= flow_collection | SCALAR
537 bool ns_escBackspace() => captureAs("\b", () => consumeChar(LETTER_B)); 247 /// ******
538 248 Event _parseNode({bool block: false, bool indentlessSequence: false}) {
539 // 45 249 var token = _scanner.peek();
540 bool ns_escHorizontalTab() => captureAs("\t", () { 250
541 return consume((c) => c == LETTER_T || c == TAB); 251 if (token is AliasToken) {
542 }); 252 _scanner.scan();
543 253 _state = _states.removeLast();
544 // 46 254 return new AliasEvent(token.span, token.name);
545 bool ns_escLineFeed() => captureAs("\n", () => consumeChar(LETTER_N)); 255 }
546 256
547 // 47 257 var anchor;
548 bool ns_escVerticalTab() => captureAs("\v", () => consumeChar(LETTER_V)); 258 var tagToken;
549 259 var span = token.span.start.pointSpan();
550 // 48 260 if (token is AnchorToken) {
Bob Nystrom 2014/10/31 20:03:27 Lot of copy/paste here. How about local functions
nweiz 2014/11/04 22:19:36 Done.
551 bool ns_escFormFeed() => captureAs("\f", () => consumeChar(LETTER_F)); 261 anchor = token.name;
552 262 span = token.span;
553 // 49 263 _scanner.scan();
554 bool ns_escCarriageReturn() => captureAs("\r", () => consumeChar(LETTER_R)); 264 token = _scanner.peek();
555 265
556 // 50 266 if (token is TagToken) {
557 bool ns_escEscape() => captureAs("\x1B", () => consumeChar(LETTER_E)); 267 tagToken = token;
558 268 span = span.expand(token.span);
559 // 51 269 _scanner.scan();
560 bool ns_escSpace() => consumeChar(SP); 270 token = _scanner.peek();
561 271 }
562 // 52 272 } else if (token is TagToken) {
563 bool ns_escDoubleQuote() => consumeChar(DOUBLE_QUOTE); 273 tagToken = token;
564 274 span = span.expand(token.span);
565 // 53 275 _scanner.scan();
566 bool ns_escSlash() => consumeChar(SLASH); 276 token = _scanner.peek();
567 277
568 // 54 278 if (token is AnchorToken) {
569 bool ns_escBackslash() => consumeChar(BACKSLASH); 279 anchor = token.name;
570 280 span = token.span;
571 // 55 281 _scanner.scan();
572 bool ns_escNextLine() => captureAs("\x85", () => consumeChar(LETTER_CAP_N)); 282 token = _scanner.peek();
573 283 }
574 // 56 284 }
575 bool ns_escNonBreakingSpace() => 285
576 captureAs("\xA0", () => consumeChar(UNDERSCORE)); 286 var tag;
577 287 if (tagToken != null) {
578 // 57 288 if (tagToken.handle == null) {
579 bool ns_escLineSeparator() => 289 tag = tagToken.suffix;
580 captureAs("\u2028", () => consumeChar(LETTER_CAP_L)); 290 } else {
581 291 var tagDirective = _tagDirectives[tagToken.handle];
582 // 58 292 if (tagDirective == null) {
583 bool ns_escParagraphSeparator() => 293 throw new YamlException("Undefined tag handle.", tagToken.span);
584 captureAs("\u2029", () => consumeChar(LETTER_CAP_P));
585
586 // 59
587 bool ns_esc8Bit() => ns_escNBit(LETTER_X, 2);
588
589 // 60
590 bool ns_esc16Bit() => ns_escNBit(LETTER_U, 4);
591
592 // 61
593 bool ns_esc32Bit() => ns_escNBit(LETTER_CAP_U, 8);
594
595 // Helper method for 59 - 61
596 bool ns_escNBit(int char, int digits) {
597 if (!captureAs('', () => consumeChar(char))) return false;
598 var captured = captureAndTransform(
599 () => nAtOnce(digits, (c, _) => isHexDigit(c)),
600 (hex) => new String.fromCharCodes([int.parse("0x$hex")]));
601 return expect(captured, "$digits hexidecimal digits");
602 }
603
604 // 62
605 bool c_ns_escChar() => context('escape sequence', () => transaction(() {
606 if (!truth(c_escape())) return false;
607 return truth(or([
608 ns_escNull, ns_escBell, ns_escBackspace, ns_escHorizontalTab,
609 ns_escLineFeed, ns_escVerticalTab, ns_escFormFeed, ns_escCarriageReturn,
610 ns_escEscape, ns_escSpace, ns_escDoubleQuote, ns_escSlash,
611 ns_escBackslash, ns_escNextLine, ns_escNonBreakingSpace,
612 ns_escLineSeparator, ns_escParagraphSeparator, ns_esc8Bit, ns_esc16Bit,
613 ns_esc32Bit
614 ]));
615 }));
616
617 // 63
618 bool s_indent(int indent) {
619 var result = nAtOnce(indent, (c, i) => c == SP);
620 if (peek() == TAB) {
621 annotateError("tab characters are not allowed as indentation in YAML",
622 () => zeroOrMore(() => consume(isSpace)));
623 }
624 return result;
625 }
626
627 // 64
628 bool s_indentLessThan(int indent) {
629 for (int i = 0; i < indent - 1; i++) {
630 if (!consumeChar(SP)) {
631 if (peek() == TAB) {
632 annotateError("tab characters are not allowed as indentation in YAML",
633 () {
634 for (; i < indent - 1; i++) {
635 if (!consume(isSpace)) break;
636 }
637 });
638 } 294 }
639 break; 295
640 } 296 tag = tagDirective.prefix + tagToken.suffix;
641 } 297 }
642 return true; 298 }
643 } 299
644 300 if (indentlessSequence && token.type == TokenType.BLOCK_ENTRY) {
645 // 65 301 _state = _State.INDENTLESS_SEQUENCE_ENTRY;
646 bool s_indentLessThanOrEqualTo(int indent) => s_indentLessThan(indent + 1); 302 return new SequenceStartEvent(
647 303 span.expand(token.span), CollectionStyle.BLOCK,
648 // 66 304 anchor: anchor, tag: tag);
649 bool s_separateInLine() => transaction(() { 305 }
650 return captureAs('', () => 306
651 truth(oneOrMore(() => consume(isSpace))) || atStartOfLine); 307 if (token is ScalarToken) {
652 }); 308 // All non-plain scalars have the "!" tag by default.
653 309 if (tag == null && token.style != ScalarStyle.PLAIN) tag = "!";
654 // 67 310
655 bool s_linePrefix(int indent, int ctx) => captureAs("", () { 311 _state = _states.removeLast();
656 switch (ctx) { 312 _scanner.scan();
657 case BLOCK_OUT: 313 return new ScalarEvent(
658 case BLOCK_IN: 314 span.expand(token.span), token.value, token.style,
659 return s_blockLinePrefix(indent); 315 anchor: anchor, tag: tag);
660 case FLOW_OUT: 316 }
661 case FLOW_IN: 317
662 return s_flowLinePrefix(indent); 318 if (token.type == TokenType.FLOW_SEQUENCE_START) {
663 } 319 _state = _State.FLOW_SEQUENCE_FIRST_ENTRY;
664 }); 320 return new SequenceStartEvent(
665 321 span.expand(token.span), CollectionStyle.FLOW,
666 // 68 322 anchor: anchor, tag: tag);
667 bool s_blockLinePrefix(int indent) => s_indent(indent); 323 }
668 324
669 // 69 325 if (token.type == TokenType.FLOW_MAPPING_START) {
670 bool s_flowLinePrefix(int indent) => captureAs('', () { 326 _state = _State.FLOW_MAPPING_FIRST_KEY;
671 if (!truth(s_indent(indent))) return false; 327 return new MappingStartEvent(
672 zeroOrOne(s_separateInLine); 328 span.expand(token.span), CollectionStyle.FLOW,
673 return true; 329 anchor: anchor, tag: tag);
674 }); 330 }
675 331
676 // 70 332 if (block && token.type == TokenType.BLOCK_SEQUENCE_START) {
677 bool l_empty(int indent, int ctx) => transaction(() { 333 _state = _State.BLOCK_SEQUENCE_FIRST_ENTRY;
678 var start = or([ 334 return new SequenceStartEvent(
679 () => s_linePrefix(indent, ctx), 335 span.expand(token.span), CollectionStyle.BLOCK,
680 () => s_indentLessThan(indent) 336 anchor: anchor, tag: tag);
681 ]); 337 }
682 if (!truth(start)) return false; 338
683 return b_asLineFeed(); 339
684 }); 340 if (block && token.type == TokenType.BLOCK_MAPPING_START) {
685 341 _state = _State.BLOCK_MAPPING_FIRST_KEY;
686 // 71 342 return new MappingStartEvent(
687 bool b_asSpace() => captureAs(" ", () => consume(isBreak)); 343 span.expand(token.span), CollectionStyle.BLOCK,
688 344 anchor: anchor, tag: tag);
689 // 72 345 }
690 bool b_l_trimmed(int indent, int ctx) => transaction(() { 346
691 if (!truth(b_nonContent())) return false; 347 if (anchor != null || tag != null) {
692 return truth(oneOrMore(() => captureAs("\n", () => l_empty(indent, ctx)))); 348 _state = _states.removeLast();
693 }); 349 return new ScalarEvent(
694 350 span, '', ScalarStyle.PLAIN,
695 // 73 351 anchor: anchor, tag: tag);
696 bool b_l_folded(int indent, int ctx) => 352 }
697 or([() => b_l_trimmed(indent, ctx), b_asSpace]); 353
698 354 throw new YamlException("Expected node content.", span);
699 // 74 355 }
700 bool s_flowFolded(int indent) => transaction(() { 356
701 zeroOrOne(s_separateInLine); 357 /// Parses the productions:
702 if (!truth(b_l_folded(indent, FLOW_IN))) return false; 358 ///
703 return s_flowLinePrefix(indent); 359 /// block_sequence ::=
704 }); 360 /// BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
705 361 /// ******************** *********** * *********
706 // 75 362 Event _parseBlockSequenceEntry({bool first: false}) {
707 bool c_nb_commentText() { 363 if (first) _scanner.scan();
708 if (!truth(c_indicator(C_COMMENT))) return false; 364 var token = _scanner.peek();
709 zeroOrMore(() => consume(isNonBreak)); 365
710 return true; 366 if (token.type == TokenType.BLOCK_ENTRY) {
711 } 367 _scanner.scan();
712 368 token = _scanner.peek();
Bob Nystrom 2014/10/31 20:03:27 This two line pattern is really common. I assume .
nweiz 2014/11/04 22:19:36 Done.
713 // 76 369
714 bool b_comment() => _scanner.isDone || b_nonContent(); 370 if (token.type == TokenType.BLOCK_ENTRY ||
715 371 token.type == TokenType.BLOCK_END) {
716 // 77 372 _state = _State.BLOCK_SEQUENCE_ENTRY;
717 bool s_b_comment() { 373 return _processEmptyScalar(token.span.end);
718 if (truth(s_separateInLine())) {
719 zeroOrOne(c_nb_commentText);
720 }
721 return b_comment();
722 }
723
724 // 78
725 bool l_comment() => transaction(() {
726 if (!truth(s_separateInLine())) return false;
727 zeroOrOne(c_nb_commentText);
728 return b_comment();
729 });
730
731 // 79
732 bool s_l_comments() {
733 if (!truth(s_b_comment()) && !atStartOfLine) return false;
734 zeroOrMore(l_comment);
735 return true;
736 }
737
738 // 80
739 bool s_separate(int indent, int ctx) {
740 switch (ctx) {
741 case BLOCK_OUT:
742 case BLOCK_IN:
743 case FLOW_OUT:
744 case FLOW_IN:
745 return s_separateLines(indent);
746 case BLOCK_KEY:
747 case FLOW_KEY:
748 return s_separateInLine();
749 default: throw 'Invalid context "$ctx".';
750 }
751 }
752
753 // 81
754 bool s_separateLines(int indent) {
755 return transaction(() => s_l_comments() && s_flowLinePrefix(indent)) ||
756 s_separateInLine();
757 }
758
759 // 82
760 bool l_directive() => false; // TODO(nweiz): implement
761
762 // 96
763 Pair<Tag, String> c_ns_properties(int indent, int ctx) {
764 var tag, anchor;
765 tag = c_ns_tagProperty();
766 if (truth(tag)) {
767 anchor = transaction(() {
768 if (!truth(s_separate(indent, ctx))) return null;
769 return c_ns_anchorProperty();
770 });
771 return new Pair<Tag, String>(tag, anchor);
772 }
773
774 anchor = c_ns_anchorProperty();
775 if (truth(anchor)) {
776 tag = transaction(() {
777 if (!truth(s_separate(indent, ctx))) return null;
778 return c_ns_tagProperty();
779 });
780 return new Pair<Tag, String>(tag, anchor);
781 }
782
783 return null;
784 }
785
786 // 97
787 Tag c_ns_tagProperty() => null; // TODO(nweiz): implement
788
789 // 101
790 String c_ns_anchorProperty() => null; // TODO(nweiz): implement
791
792 // 102
793 bool isAnchorChar(int char) => isNonSpace(char) && !isFlowIndicator(char);
794
795 // 103
796 String ns_anchorName() =>
797 captureString(() => oneOrMore(() => consume(isAnchorChar)));
798
799 // 104
800 Node c_ns_aliasNode() {
801 var start = _scanner.state;
802 if (!truth(c_indicator(C_ALIAS))) return null;
803 var name = expect(ns_anchorName(), 'anchor name');
804 return new AliasNode(name, _scanner.spanFrom(start));
805 }
806
807 // 105
808 ScalarNode e_scalar() => new ScalarNode("?", _scanner.emptySpan, content: "");
809
810 // 106
811 ScalarNode e_node() => e_scalar();
812
813 // 107
814 bool nb_doubleChar() => or([
815 c_ns_escChar,
816 () => consume((c) => isJson(c) && c != BACKSLASH && c != DOUBLE_QUOTE)
817 ]);
818
819 // 108
820 bool ns_doubleChar() => !isSpace(peek()) && truth(nb_doubleChar());
821
822 // 109
823 Node c_doubleQuoted(int indent, int ctx) => context('string', () {
824 return transaction(() {
825 var start = _scanner.state;
826 if (!truth(c_indicator(C_DOUBLE_QUOTE))) return null;
827 var contents = nb_doubleText(indent, ctx);
828 if (!truth(c_indicator(C_DOUBLE_QUOTE))) return null;
829 return new ScalarNode("!", _scanner.spanFrom(start), content: contents);
830 });
831 });
832
833 // 110
834 String nb_doubleText(int indent, int ctx) => captureString(() {
835 switch (ctx) {
836 case FLOW_OUT:
837 case FLOW_IN:
838 nb_doubleMultiLine(indent);
839 break;
840 case BLOCK_KEY:
841 case FLOW_KEY:
842 nb_doubleOneLine();
843 break;
844 }
845 return true;
846 });
847
848 // 111
849 void nb_doubleOneLine() {
850 zeroOrMore(nb_doubleChar);
851 }
852
853 // 112
854 bool s_doubleEscaped(int indent) => transaction(() {
855 zeroOrMore(() => consume(isSpace));
856 if (!captureAs("", () => consumeChar(BACKSLASH))) return false;
857 if (!truth(b_nonContent())) return false;
858 zeroOrMore(() => captureAs("\n", () => l_empty(indent, FLOW_IN)));
859 return s_flowLinePrefix(indent);
860 });
861
862 // 113
863 bool s_doubleBreak(int indent) => or([
864 () => s_doubleEscaped(indent),
865 () => s_flowFolded(indent)
866 ]);
867
868 // 114
869 void nb_ns_doubleInLine() {
870 zeroOrMore(() => transaction(() {
871 zeroOrMore(() => consume(isSpace));
872 return ns_doubleChar();
873 }));
874 }
875
876 // 115
877 bool s_doubleNextLine(int indent) {
878 if (!truth(s_doubleBreak(indent))) return false;
879 zeroOrOne(() {
880 if (!truth(ns_doubleChar())) return;
881 nb_ns_doubleInLine();
882 or([
883 () => s_doubleNextLine(indent),
884 () => zeroOrMore(() => consume(isSpace))
885 ]);
886 });
887 return true;
888 }
889
890 // 116
891 void nb_doubleMultiLine(int indent) {
892 nb_ns_doubleInLine();
893 or([
894 () => s_doubleNextLine(indent),
895 () => zeroOrMore(() => consume(isSpace))
896 ]);
897 }
898
899 // 117
900 bool c_quotedQuote() => captureAs("'", () => rawString("''"));
901
902 // 118
903 bool nb_singleChar() => or([
904 c_quotedQuote,
905 () => consume((c) => isJson(c) && c != SINGLE_QUOTE)
906 ]);
907
908 // 119
909 bool ns_singleChar() => !isSpace(peek()) && truth(nb_singleChar());
910
911 // 120
912 Node c_singleQuoted(int indent, int ctx) => context('string', () {
913 return transaction(() {
914 var start = _scanner.state;
915 if (!truth(c_indicator(C_SINGLE_QUOTE))) return null;
916 var contents = nb_singleText(indent, ctx);
917 if (!truth(c_indicator(C_SINGLE_QUOTE))) return null;
918 return new ScalarNode("!", _scanner.spanFrom(start), content: contents);
919 });
920 });
921
922 // 121
923 String nb_singleText(int indent, int ctx) => captureString(() {
924 switch (ctx) {
925 case FLOW_OUT:
926 case FLOW_IN:
927 nb_singleMultiLine(indent);
928 break;
929 case BLOCK_KEY:
930 case FLOW_KEY:
931 nb_singleOneLine(indent);
932 break;
933 }
934 return true;
935 });
936
937 // 122
938 void nb_singleOneLine(int indent) {
939 zeroOrMore(nb_singleChar);
940 }
941
942 // 123
943 void nb_ns_singleInLine() {
944 zeroOrMore(() => transaction(() {
945 zeroOrMore(() => consume(isSpace));
946 return ns_singleChar();
947 }));
948 }
949
950 // 124
951 bool s_singleNextLine(int indent) {
952 if (!truth(s_flowFolded(indent))) return false;
953 zeroOrOne(() {
954 if (!truth(ns_singleChar())) return;
955 nb_ns_singleInLine();
956 or([
957 () => s_singleNextLine(indent),
958 () => zeroOrMore(() => consume(isSpace))
959 ]);
960 });
961 return true;
962 }
963
964 // 125
965 void nb_singleMultiLine(int indent) {
966 nb_ns_singleInLine();
967 or([
968 () => s_singleNextLine(indent),
969 () => zeroOrMore(() => consume(isSpace))
970 ]);
971 }
972
973 // 126
974 bool ns_plainFirst(int ctx) {
975 var char = peek();
976 var indicator = indicatorType(char);
977 if (indicator == C_RESERVED) {
978 error("Reserved indicators can't start a plain scalar");
979 }
980 var match = (isNonSpace(char) && indicator == null) ||
981 ((indicator == C_MAPPING_KEY ||
982 indicator == C_MAPPING_VALUE ||
983 indicator == C_SEQUENCE_ENTRY) &&
984 isPlainSafe(ctx, peek(1)));
985
986 if (match) next();
987 return match;
988 }
989
990 // 127
991 bool isPlainSafe(int ctx, int char) {
992 switch (ctx) {
993 case FLOW_OUT:
994 case BLOCK_KEY:
995 // 128
996 return isNonSpace(char);
997 case FLOW_IN:
998 case FLOW_KEY:
999 // 129
1000 return isNonSpace(char) && !isFlowIndicator(char);
1001 default: throw 'Invalid context "$ctx".';
1002 }
1003 }
1004
1005 // 130
1006 bool ns_plainChar(int ctx) {
1007 var char = peek();
1008 var indicator = indicatorType(char);
1009 var safeChar = isPlainSafe(ctx, char) && indicator != C_MAPPING_VALUE &&
1010 indicator != C_COMMENT;
1011 var nonCommentHash = isNonSpace(peek(-1)) && indicator == C_COMMENT;
1012 var nonMappingColon = indicator == C_MAPPING_VALUE &&
1013 isPlainSafe(ctx, peek(1));
1014 var match = safeChar || nonCommentHash || nonMappingColon;
1015
1016 if (match) next();
1017 return match;
1018 }
1019
1020 // 131
1021 String ns_plain(int indent, int ctx) => context('plain scalar', () {
1022 return captureString(() {
1023 switch (ctx) {
1024 case FLOW_OUT:
1025 case FLOW_IN:
1026 return ns_plainMultiLine(indent, ctx);
1027 case BLOCK_KEY:
1028 case FLOW_KEY:
1029 return ns_plainOneLine(ctx);
1030 default: throw 'Invalid context "$ctx".';
1031 }
1032 });
1033 });
1034
1035 // 132
1036 void nb_ns_plainInLine(int ctx) {
1037 zeroOrMore(() => transaction(() {
1038 zeroOrMore(() => consume(isSpace));
1039 return ns_plainChar(ctx);
1040 }));
1041 }
1042
1043 // 133
1044 bool ns_plainOneLine(int ctx) {
1045 if (truth(c_forbidden())) return false;
1046 if (!truth(ns_plainFirst(ctx))) return false;
1047 nb_ns_plainInLine(ctx);
1048 return true;
1049 }
1050
1051 // 134
1052 bool s_ns_plainNextLine(int indent, int ctx) => transaction(() {
1053 if (!truth(s_flowFolded(indent))) return false;
1054 if (truth(c_forbidden())) return false;
1055 if (!truth(ns_plainChar(ctx))) return false;
1056 nb_ns_plainInLine(ctx);
1057 return true;
1058 });
1059
1060 // 135
1061 bool ns_plainMultiLine(int indent, int ctx) {
1062 if (!truth(ns_plainOneLine(ctx))) return false;
1063 zeroOrMore(() => s_ns_plainNextLine(indent, ctx));
1064 return true;
1065 }
1066
1067 // 136
1068 int inFlow(int ctx) {
1069 switch (ctx) {
1070 case FLOW_OUT:
1071 case FLOW_IN:
1072 return FLOW_IN;
1073 case BLOCK_KEY:
1074 case FLOW_KEY:
1075 return FLOW_KEY;
1076 }
1077 throw "unreachable";
1078 }
1079
1080 // 137
1081 SequenceNode c_flowSequence(int indent, int ctx) => transaction(() {
1082 var start = _scanner.state;
1083 if (!truth(c_indicator(C_SEQUENCE_START))) return null;
1084 zeroOrOne(() => s_separate(indent, ctx));
1085 var content = zeroOrOne(() => ns_s_flowSeqEntries(indent, inFlow(ctx)));
1086 if (!truth(c_indicator(C_SEQUENCE_END))) return null;
1087 return new SequenceNode("?", new List<Node>.from(content),
1088 _scanner.spanFrom(start));
1089 });
1090
1091 // 138
1092 Iterable<Node> ns_s_flowSeqEntries(int indent, int ctx) {
1093 var first = ns_flowSeqEntry(indent, ctx);
1094 if (!truth(first)) return new Queue<Node>();
1095 zeroOrOne(() => s_separate(indent, ctx));
1096
1097 var rest;
1098 if (truth(c_indicator(C_COLLECT_ENTRY))) {
1099 zeroOrOne(() => s_separate(indent, ctx));
1100 rest = zeroOrOne(() => ns_s_flowSeqEntries(indent, ctx));
1101 }
1102
1103 if (rest == null) rest = new Queue<Node>();
1104 rest.addFirst(first);
1105
1106 return rest;
1107 }
1108
1109 // 139
1110 Node ns_flowSeqEntry(int indent, int ctx) => or([
1111 () => ns_flowPair(indent, ctx),
1112 () => ns_flowNode(indent, ctx)
1113 ]);
1114
1115 // 140
1116 Node c_flowMapping(int indent, int ctx) {
1117 var start = _scanner.state;
1118 if (!truth(c_indicator(C_MAPPING_START))) return null;
1119 zeroOrOne(() => s_separate(indent, ctx));
1120 var content = zeroOrOne(() => ns_s_flowMapEntries(indent, inFlow(ctx)));
1121 if (!truth(c_indicator(C_MAPPING_END))) return null;
1122 return new MappingNode("?", content, _scanner.spanFrom(start));
1123 }
1124
1125 // 141
1126 Map ns_s_flowMapEntries(int indent, int ctx) {
1127 var first = ns_flowMapEntry(indent, ctx);
1128 if (!truth(first)) return deepEqualsMap();
1129 zeroOrOne(() => s_separate(indent, ctx));
1130
1131 var rest;
1132 if (truth(c_indicator(C_COLLECT_ENTRY))) {
1133 zeroOrOne(() => s_separate(indent, ctx));
1134 rest = ns_s_flowMapEntries(indent, ctx);
1135 }
1136
1137 if (rest == null) rest = deepEqualsMap();
1138
1139 // TODO(nweiz): Duplicate keys should be an error. This includes keys with
1140 // different representations but the same value (e.g. 10 vs 0xa). To make
1141 // this user-friendly we'll probably also want to associate nodes with a
1142 // source range.
1143 if (!rest.containsKey(first.first)) rest[first.first] = first.last;
1144
1145 return rest;
1146 }
1147
1148 // 142
1149 Pair<Node, Node> ns_flowMapEntry(int indent, int ctx) => or([
1150 () => transaction(() {
1151 if (!truth(c_indicator(C_MAPPING_KEY))) return false;
1152 if (!truth(s_separate(indent, ctx))) return false;
1153 return ns_flowMapExplicitEntry(indent, ctx);
1154 }),
1155 () => ns_flowMapImplicitEntry(indent, ctx)
1156 ]);
1157
1158 // 143
1159 Pair<Node, Node> ns_flowMapExplicitEntry(int indent, int ctx) => or([
1160 () => ns_flowMapImplicitEntry(indent, ctx),
1161 () => new Pair<Node, Node>(e_node(), e_node())
1162 ]);
1163
1164 // 144
1165 Pair<Node, Node> ns_flowMapImplicitEntry(int indent, int ctx) => or([
1166 () => ns_flowMapYamlKeyEntry(indent, ctx),
1167 () => c_ns_flowMapEmptyKeyEntry(indent, ctx),
1168 () => c_ns_flowMapJsonKeyEntry(indent, ctx)
1169 ]);
1170
1171 // 145
1172 Pair<Node, Node> ns_flowMapYamlKeyEntry(int indent, int ctx) {
1173 var key = ns_flowYamlNode(indent, ctx);
1174 if (!truth(key)) return null;
1175 var value = or([
1176 () => transaction(() {
1177 zeroOrOne(() => s_separate(indent, ctx));
1178 return c_ns_flowMapSeparateValue(indent, ctx);
1179 }),
1180 e_node
1181 ]);
1182 return new Pair<Node, Node>(key, value);
1183 }
1184
1185 // 146
1186 Pair<Node, Node> c_ns_flowMapEmptyKeyEntry(int indent, int ctx) {
1187 var value = c_ns_flowMapSeparateValue(indent, ctx);
1188 if (!truth(value)) return null;
1189 return new Pair<Node, Node>(e_node(), value);
1190 }
1191
1192 // 147
1193 Node c_ns_flowMapSeparateValue(int indent, int ctx) => transaction(() {
1194 if (!truth(c_indicator(C_MAPPING_VALUE))) return null;
1195 if (isPlainSafe(ctx, peek())) return null;
1196
1197 return or([
1198 () => transaction(() {
1199 if (!s_separate(indent, ctx)) return null;
1200 return ns_flowNode(indent, ctx);
1201 }),
1202 e_node
1203 ]);
1204 });
1205
1206 // 148
1207 Pair<Node, Node> c_ns_flowMapJsonKeyEntry(int indent, int ctx) {
1208 var key = c_flowJsonNode(indent, ctx);
1209 if (!truth(key)) return null;
1210 var value = or([
1211 () => transaction(() {
1212 zeroOrOne(() => s_separate(indent, ctx));
1213 return c_ns_flowMapAdjacentValue(indent, ctx);
1214 }),
1215 e_node
1216 ]);
1217 return new Pair<Node, Node>(key, value);
1218 }
1219
1220 // 149
1221 Node c_ns_flowMapAdjacentValue(int indent, int ctx) {
1222 if (!truth(c_indicator(C_MAPPING_VALUE))) return null;
1223 return or([
1224 () => transaction(() {
1225 zeroOrOne(() => s_separate(indent, ctx));
1226 return ns_flowNode(indent, ctx);
1227 }),
1228 e_node
1229 ]);
1230 }
1231
1232 // 150
1233 Node ns_flowPair(int indent, int ctx) {
1234 var start = _scanner.state;
1235 var pair = or([
1236 () => transaction(() {
1237 if (!truth(c_indicator(C_MAPPING_KEY))) return null;
1238 if (!truth(s_separate(indent, ctx))) return null;
1239 return ns_flowMapExplicitEntry(indent, ctx);
1240 }),
1241 () => ns_flowPairEntry(indent, ctx)
1242 ]);
1243 if (!truth(pair)) return null;
1244
1245 return map([pair], _scanner.spanFrom(start));
1246 }
1247
1248 // 151
1249 Pair<Node, Node> ns_flowPairEntry(int indent, int ctx) => or([
1250 () => ns_flowPairYamlKeyEntry(indent, ctx),
1251 () => c_ns_flowMapEmptyKeyEntry(indent, ctx),
1252 () => c_ns_flowPairJsonKeyEntry(indent, ctx)
1253 ]);
1254
1255 // 152
1256 Pair<Node, Node> ns_flowPairYamlKeyEntry(int indent, int ctx) =>
1257 transaction(() {
1258 var key = ns_s_implicitYamlKey(FLOW_KEY);
1259 if (!truth(key)) return null;
1260 var value = c_ns_flowMapSeparateValue(indent, ctx);
1261 if (!truth(value)) return null;
1262 return new Pair<Node, Node>(key, value);
1263 });
1264
1265 // 153
1266 Pair<Node, Node> c_ns_flowPairJsonKeyEntry(int indent, int ctx) =>
1267 transaction(() {
1268 var key = c_s_implicitJsonKey(FLOW_KEY);
1269 if (!truth(key)) return null;
1270 var value = c_ns_flowMapAdjacentValue(indent, ctx);
1271 if (!truth(value)) return null;
1272 return new Pair<Node, Node>(key, value);
1273 });
1274
1275 // 154
1276 Node ns_s_implicitYamlKey(int ctx) => transaction(() {
1277 // TODO(nweiz): this is supposed to be limited to 1024 characters.
1278
1279 // The indentation parameter is "null" since it's unused in this path
1280 var node = ns_flowYamlNode(null, ctx);
1281 if (!truth(node)) return null;
1282 zeroOrOne(s_separateInLine);
1283 return node;
1284 });
1285
1286 // 155
1287 Node c_s_implicitJsonKey(int ctx) => transaction(() {
1288 // TODO(nweiz): this is supposed to be limited to 1024 characters.
1289
1290 // The indentation parameter is "null" since it's unused in this path
1291 var node = c_flowJsonNode(null, ctx);
1292 if (!truth(node)) return null;
1293 zeroOrOne(s_separateInLine);
1294 return node;
1295 });
1296
1297 // 156
1298 Node ns_flowYamlContent(int indent, int ctx) {
1299 var start = _scanner.state;
1300 var str = ns_plain(indent, ctx);
1301 if (!truth(str)) return null;
1302 return new ScalarNode("?", _scanner.spanFrom(start), content: str);
1303 }
1304
1305 // 157
1306 Node c_flowJsonContent(int indent, int ctx) => or([
1307 () => c_flowSequence(indent, ctx),
1308 () => c_flowMapping(indent, ctx),
1309 () => c_singleQuoted(indent, ctx),
1310 () => c_doubleQuoted(indent, ctx)
1311 ]);
1312
1313 // 158
1314 Node ns_flowContent(int indent, int ctx) => or([
1315 () => ns_flowYamlContent(indent, ctx),
1316 () => c_flowJsonContent(indent, ctx)
1317 ]);
1318
1319 // 159
1320 Node ns_flowYamlNode(int indent, int ctx) => or([
1321 c_ns_aliasNode,
1322 () => ns_flowYamlContent(indent, ctx),
1323 () {
1324 var props = c_ns_properties(indent, ctx);
1325 if (!truth(props)) return null;
1326 var node = or([
1327 () => transaction(() {
1328 if (!truth(s_separate(indent, ctx))) return null;
1329 return ns_flowYamlContent(indent, ctx);
1330 }),
1331 e_scalar
1332 ]);
1333 return addProps(node, props);
1334 }
1335 ]);
1336
1337 // 160
1338 Node c_flowJsonNode(int indent, int ctx) => transaction(() {
1339 var props;
1340 zeroOrOne(() => transaction(() {
1341 props = c_ns_properties(indent, ctx);
1342 if (!truth(props)) return null;
1343 return s_separate(indent, ctx);
1344 }));
1345
1346 return addProps(c_flowJsonContent(indent, ctx), props);
1347 });
1348
1349 // 161
1350 Node ns_flowNode(int indent, int ctx) => or([
1351 c_ns_aliasNode,
1352 () => ns_flowContent(indent, ctx),
1353 () => transaction(() {
1354 var props = c_ns_properties(indent, ctx);
1355 if (!truth(props)) return null;
1356 var node = or([
1357 () => transaction(() => s_separate(indent, ctx) ?
1358 ns_flowContent(indent, ctx) : null),
1359 e_scalar]);
1360 return addProps(node, props);
1361 })
1362 ]);
1363
1364 // 162
1365 _BlockHeader c_b_blockHeader() => transaction(() {
1366 var indentation = c_indentationIndicator();
1367 var chomping = c_chompingIndicator();
1368 if (!truth(indentation)) indentation = c_indentationIndicator();
1369 if (!truth(s_b_comment())) return null;
1370
1371 return new _BlockHeader(indentation, chomping);
1372 });
1373
1374 // 163
1375 int c_indentationIndicator() {
1376 if (!isDecDigit(peek())) return null;
1377 return next() - NUMBER_0;
1378 }
1379
1380 // 164
1381 int c_chompingIndicator() {
1382 switch (peek()) {
1383 case HYPHEN:
1384 next();
1385 return CHOMPING_STRIP;
1386 case PLUS:
1387 next();
1388 return CHOMPING_KEEP;
1389 default:
1390 return CHOMPING_CLIP;
1391 }
1392 }
1393
1394 // 165
1395 bool b_chompedLast(int chomping) {
1396 if (_scanner.isDone) return true;
1397 switch (chomping) {
1398 case CHOMPING_STRIP:
1399 return b_nonContent();
1400 case CHOMPING_CLIP:
1401 case CHOMPING_KEEP:
1402 return b_asLineFeed();
1403 }
1404 throw "unreachable";
1405 }
1406
1407 // 166
1408 void l_chompedEmpty(int indent, int chomping) {
1409 switch (chomping) {
1410 case CHOMPING_STRIP:
1411 case CHOMPING_CLIP:
1412 l_stripEmpty(indent);
1413 break;
1414 case CHOMPING_KEEP:
1415 l_keepEmpty(indent);
1416 break;
1417 }
1418 }
1419
1420 // 167
1421 void l_stripEmpty(int indent) {
1422 captureAs('', () {
1423 zeroOrMore(() => transaction(() {
1424 if (!truth(s_indentLessThanOrEqualTo(indent))) return false;
1425 return b_nonContent();
1426 }));
1427 zeroOrOne(() => l_trailComments(indent));
1428 return true;
1429 });
1430 }
1431
1432 // 168
1433 void l_keepEmpty(int indent) {
1434 zeroOrMore(() => captureAs('\n', () => l_empty(indent, BLOCK_IN)));
1435 zeroOrOne(() => captureAs('', () => l_trailComments(indent)));
1436 }
1437
1438 // 169
1439 bool l_trailComments(int indent) => transaction(() {
1440 if (!truth(s_indentLessThanOrEqualTo(indent))) return false;
1441 if (!truth(c_nb_commentText())) return false;
1442 if (!truth(b_comment())) return false;
1443 zeroOrMore(l_comment);
1444 return true;
1445 });
1446
1447 // 170
1448 Node c_l_literal(int indent) => transaction(() {
1449 var start = _scanner.state;
1450 if (!truth(c_indicator(C_LITERAL))) return null;
1451 var header = c_b_blockHeader();
1452 if (!truth(header)) return null;
1453
1454 var additionalIndent = blockScalarAdditionalIndentation(header, indent);
1455 var content = l_literalContent(indent + additionalIndent, header.chomping);
1456 if (!truth(content)) return null;
1457
1458 return new ScalarNode("!", _scanner.spanFrom(start), content: content);
1459 });
1460
1461 // 171
1462 bool l_nb_literalText(int indent) => transaction(() {
1463 zeroOrMore(() => captureAs("\n", () => l_empty(indent, BLOCK_IN)));
1464 if (!truth(captureAs("", () => s_indent(indent)))) return false;
1465 return truth(oneOrMore(() => consume(isNonBreak)));
1466 });
1467
1468 // 172
1469 bool b_nb_literalNext(int indent) => transaction(() {
1470 if (!truth(b_asLineFeed())) return false;
1471 return l_nb_literalText(indent);
1472 });
1473
1474 // 173
1475 String l_literalContent(int indent, int chomping) => captureString(() {
1476 transaction(() {
1477 if (!truth(l_nb_literalText(indent))) return false;
1478 zeroOrMore(() => b_nb_literalNext(indent));
1479 return b_chompedLast(chomping);
1480 });
1481 l_chompedEmpty(indent, chomping);
1482 return true;
1483 });
1484
1485 // 174
1486 Node c_l_folded(int indent) => transaction(() {
1487 var start = _scanner.state;
1488 if (!truth(c_indicator(C_FOLDED))) return null;
1489 var header = c_b_blockHeader();
1490 if (!truth(header)) return null;
1491
1492 var additionalIndent = blockScalarAdditionalIndentation(header, indent);
1493 var content = l_foldedContent(indent + additionalIndent, header.chomping);
1494 if (!truth(content)) return null;
1495
1496 return new ScalarNode("!", _scanner.spanFrom(start), content: content);
1497 });
1498
1499 // 175
1500 bool s_nb_foldedText(int indent) => transaction(() {
1501 if (!truth(captureAs('', () => s_indent(indent)))) return false;
1502 if (!truth(consume(isNonSpace))) return false;
1503 zeroOrMore(() => consume(isNonBreak));
1504 return true;
1505 });
1506
1507 // 176
1508 bool l_nb_foldedLines(int indent) {
1509 if (!truth(s_nb_foldedText(indent))) return false;
1510 zeroOrMore(() => transaction(() {
1511 if (!truth(b_l_folded(indent, BLOCK_IN))) return false;
1512 return s_nb_foldedText(indent);
1513 }));
1514 return true;
1515 }
1516
1517 // 177
1518 bool s_nb_spacedText(int indent) => transaction(() {
1519 if (!truth(captureAs('', () => s_indent(indent)))) return false;
1520 if (!truth(consume(isSpace))) return false;
1521 zeroOrMore(() => consume(isNonBreak));
1522 return true;
1523 });
1524
1525 // 178
1526 bool b_l_spaced(int indent) {
1527 if (!truth(b_asLineFeed())) return false;
1528 zeroOrMore(() => captureAs("\n", () => l_empty(indent, BLOCK_IN)));
1529 return true;
1530 }
1531
1532 // 179
1533 bool l_nb_spacedLines(int indent) {
1534 if (!truth(s_nb_spacedText(indent))) return false;
1535 zeroOrMore(() => transaction(() {
1536 if (!truth(b_l_spaced(indent))) return false;
1537 return s_nb_spacedText(indent);
1538 }));
1539 return true;
1540 }
1541
1542 // 180
1543 bool l_nb_sameLines(int indent) => transaction(() {
1544 zeroOrMore(() => captureAs('\n', () => l_empty(indent, BLOCK_IN)));
1545 return or([
1546 () => l_nb_foldedLines(indent),
1547 () => l_nb_spacedLines(indent)
1548 ]);
1549 });
1550
1551 // 181
1552 bool l_nb_diffLines(int indent) {
1553 if (!truth(l_nb_sameLines(indent))) return false;
1554 zeroOrMore(() => transaction(() {
1555 if (!truth(b_asLineFeed())) return false;
1556 return l_nb_sameLines(indent);
1557 }));
1558 return true;
1559 }
1560
1561 // 182
1562 String l_foldedContent(int indent, int chomping) => captureString(() {
1563 transaction(() {
1564 if (!truth(l_nb_diffLines(indent))) return false;
1565 return b_chompedLast(chomping);
1566 });
1567 l_chompedEmpty(indent, chomping);
1568 return true;
1569 });
1570
1571 // 183
1572 SequenceNode l_blockSequence(int indent) => context('sequence', () {
1573 var additionalIndent = countIndentation() - indent;
1574 if (additionalIndent <= 0) return null;
1575
1576 var start = _scanner.state;
1577 var content = oneOrMore(() => transaction(() {
1578 if (!truth(s_indent(indent + additionalIndent))) return null;
1579 return c_l_blockSeqEntry(indent + additionalIndent);
1580 }));
1581 if (!truth(content)) return null;
1582
1583 return new SequenceNode("?", content, _scanner.spanFrom(start));
1584 });
1585
1586 // 184
1587 Node c_l_blockSeqEntry(int indent) => transaction(() {
1588 if (!truth(c_indicator(C_SEQUENCE_ENTRY))) return null;
1589 if (isNonSpace(peek())) return null;
1590
1591 return s_l_blockIndented(indent, BLOCK_IN);
1592 });
1593
1594 // 185
1595 Node s_l_blockIndented(int indent, int ctx) {
1596 var additionalIndent = countIndentation();
1597 return or([
1598 () => transaction(() {
1599 if (!truth(s_indent(additionalIndent))) return null;
1600 return or([
1601 () => ns_l_compactSequence(indent + 1 + additionalIndent),
1602 () => ns_l_compactMapping(indent + 1 + additionalIndent)]);
1603 }),
1604 () => s_l_blockNode(indent, ctx),
1605 () => s_l_comments() ? e_node() : null]);
1606 }
1607
1608 // 186
1609 Node ns_l_compactSequence(int indent) => context('sequence', () {
1610 var start = _scanner.state;
1611 var first = c_l_blockSeqEntry(indent);
1612 if (!truth(first)) return null;
1613
1614 var content = zeroOrMore(() => transaction(() {
1615 if (!truth(s_indent(indent))) return null;
1616 return c_l_blockSeqEntry(indent);
1617 }));
1618 content.insert(0, first);
1619
1620 return new SequenceNode("?", content, _scanner.spanFrom(start));
1621 });
1622
1623 // 187
1624 Node l_blockMapping(int indent) => context('mapping', () {
1625 var additionalIndent = countIndentation() - indent;
1626 if (additionalIndent <= 0) return null;
1627
1628 var start = _scanner.state;
1629 var pairs = oneOrMore(() => transaction(() {
1630 if (!truth(s_indent(indent + additionalIndent))) return null;
1631 return ns_l_blockMapEntry(indent + additionalIndent);
1632 }));
1633 if (!truth(pairs)) return null;
1634
1635 return map(pairs, _scanner.spanFrom(start));
1636 });
1637
1638 // 188
1639 Pair<Node, Node> ns_l_blockMapEntry(int indent) => or([
1640 () => c_l_blockMapExplicitEntry(indent),
1641 () => ns_l_blockMapImplicitEntry(indent)
1642 ]);
1643
1644 // 189
1645 Pair<Node, Node> c_l_blockMapExplicitEntry(int indent) {
1646 var key = c_l_blockMapExplicitKey(indent);
1647 if (!truth(key)) return null;
1648
1649 var value = or([
1650 () => l_blockMapExplicitValue(indent),
1651 e_node
1652 ]);
1653
1654 return new Pair<Node, Node>(key, value);
1655 }
1656
1657 // 190
1658 Node c_l_blockMapExplicitKey(int indent) => transaction(() {
1659 if (!truth(c_indicator(C_MAPPING_KEY))) return null;
1660 return s_l_blockIndented(indent, BLOCK_OUT);
1661 });
1662
1663 // 191
1664 Node l_blockMapExplicitValue(int indent) => transaction(() {
1665 if (!truth(s_indent(indent))) return null;
1666 if (!truth(c_indicator(C_MAPPING_VALUE))) return null;
1667 return s_l_blockIndented(indent, BLOCK_OUT);
1668 });
1669
1670 // 192
1671 Pair<Node, Node> ns_l_blockMapImplicitEntry(int indent) => transaction(() {
1672 var key = or([ns_s_blockMapImplicitKey, e_node]);
1673 var value = c_l_blockMapImplicitValue(indent);
1674 return truth(value) ? new Pair<Node, Node>(key, value) : null;
1675 });
1676
1677 // 193
1678 Node ns_s_blockMapImplicitKey() => context('mapping key', () => or([
1679 () => c_s_implicitJsonKey(BLOCK_KEY),
1680 () => ns_s_implicitYamlKey(BLOCK_KEY)
1681 ]));
1682
1683 // 194
1684 Node c_l_blockMapImplicitValue(int indent) => context('mapping value', () =>
1685 transaction(() {
1686 if (!truth(c_indicator(C_MAPPING_VALUE))) return null;
1687 return or([
1688 () => s_l_blockNode(indent, BLOCK_OUT),
1689 () => s_l_comments() ? e_node() : null
1690 ]);
1691 }));
1692
1693 // 195
1694 Node ns_l_compactMapping(int indent) => context('mapping', () {
1695 var start = _scanner.state;
1696 var first = ns_l_blockMapEntry(indent);
1697 if (!truth(first)) return null;
1698
1699 var pairs = zeroOrMore(() => transaction(() {
1700 if (!truth(s_indent(indent))) return null;
1701 return ns_l_blockMapEntry(indent);
1702 }));
1703 pairs.insert(0, first);
1704
1705 return map(pairs, _scanner.spanFrom(start));
1706 });
1707
1708 // 196
1709 Node s_l_blockNode(int indent, int ctx) => or([
1710 () => s_l_blockInBlock(indent, ctx),
1711 () => s_l_flowInBlock(indent)
1712 ]);
1713
1714 // 197
1715 Node s_l_flowInBlock(int indent) => transaction(() {
1716 if (!truth(s_separate(indent + 1, FLOW_OUT))) return null;
1717 var node = ns_flowNode(indent + 1, FLOW_OUT);
1718 if (!truth(node)) return null;
1719 if (!truth(s_l_comments())) return null;
1720 return node;
1721 });
1722
1723 // 198
1724 Node s_l_blockInBlock(int indent, int ctx) => or([
1725 () => s_l_blockScalar(indent, ctx),
1726 () => s_l_blockCollection(indent, ctx)
1727 ]);
1728
1729 // 199
1730 Node s_l_blockScalar(int indent, int ctx) => transaction(() {
1731 if (!truth(s_separate(indent + 1, ctx))) return null;
1732 var props = transaction(() {
1733 var innerProps = c_ns_properties(indent + 1, ctx);
1734 if (!truth(innerProps)) return null;
1735 if (!truth(s_separate(indent + 1, ctx))) return null;
1736 return innerProps;
1737 });
1738
1739 var node = or([() => c_l_literal(indent), () => c_l_folded(indent)]);
1740 if (!truth(node)) return null;
1741 return addProps(node, props);
1742 });
1743
1744 // 200
1745 Node s_l_blockCollection(int indent, int ctx) => transaction(() {
1746 var props = transaction(() {
1747 if (!truth(s_separate(indent + 1, ctx))) return null;
1748 return c_ns_properties(indent + 1, ctx);
1749 });
1750
1751 if (!truth(s_l_comments())) return null;
1752 return or([
1753 () => l_blockSequence(seqSpaces(indent, ctx)),
1754 () => l_blockMapping(indent)]);
1755 });
1756
1757 // 201
1758 int seqSpaces(int indent, int ctx) => ctx == BLOCK_OUT ? indent - 1 : indent;
1759
1760 // 202
1761 void l_documentPrefix() {
1762 zeroOrMore(l_comment);
1763 }
1764
1765 // 203
1766 bool c_directivesEnd() => rawString("---");
1767
1768 // 204
1769 bool c_documentEnd() => rawString("...");
1770
1771 // 205
1772 bool l_documentSuffix() => transaction(() {
1773 if (!truth(c_documentEnd())) return false;
1774 return s_l_comments();
1775 });
1776
1777 // 206
1778 bool c_forbidden() {
1779 if (!_inBareDocument || !atStartOfLine) return false;
1780 var forbidden = false;
1781 transaction(() {
1782 if (!truth(or([c_directivesEnd, c_documentEnd]))) return;
1783 var char = peek();
1784 forbidden = isBreak(char) || isSpace(char) || _scanner.isDone;
1785 return;
1786 });
1787 return forbidden;
1788 }
1789
1790 // 207
1791 Node l_bareDocument() {
1792 try {
1793 _inBareDocument = true;
1794 return s_l_blockNode(-1, BLOCK_IN);
1795 } finally {
1796 _inBareDocument = false;
1797 }
1798 }
1799
1800 // 208
1801 Node l_explicitDocument() {
1802 if (!truth(c_directivesEnd())) return null;
1803 var doc = l_bareDocument();
1804 if (truth(doc)) return doc;
1805
1806 doc = e_node();
1807 s_l_comments();
1808 return doc;
1809 }
1810
1811 // 209
1812 Node l_directiveDocument() {
1813 if (!truth(oneOrMore(l_directive))) return null;
1814 var doc = l_explicitDocument();
1815 if (doc != null) return doc;
1816 parseFailed();
1817 return null; // Unreachable.
1818 }
1819
1820 // 210
1821 Node l_anyDocument() =>
1822 or([l_directiveDocument, l_explicitDocument, l_bareDocument]);
1823
1824 // 211
1825 Pair<List<Node>, SourceSpan> l_yamlStream() {
1826 var start = _scanner.state;
1827 var docs = [];
1828 zeroOrMore(l_documentPrefix);
1829 var first = zeroOrOne(l_anyDocument);
1830 if (!truth(first)) first = e_node();
1831 docs.add(first);
1832
1833 zeroOrMore(() {
1834 var doc;
1835 if (truth(oneOrMore(l_documentSuffix))) {
1836 zeroOrMore(l_documentPrefix);
1837 doc = zeroOrOne(l_anyDocument);
1838 } else { 374 } else {
1839 zeroOrMore(l_documentPrefix); 375 _states.add(_State.BLOCK_SEQUENCE_ENTRY);
1840 doc = zeroOrOne(l_explicitDocument); 376 return _parseNode(block: true);
1841 } 377 }
1842 if (truth(doc)) docs.add(doc); 378 }
1843 return doc; 379
1844 }); 380 if (token.type == TokenType.BLOCK_END) {
1845 381 _scanner.scan();
1846 if (!_scanner.isDone) parseFailed(); 382 _state = _states.removeLast();
1847 return new Pair(docs, _scanner.spanFrom(start)); 383 return new Event(EventType.SEQUENCE_END, token.span);
384 }
385
386 throw new YamlException("While parsing a block collection, expected '-'.",
387 token.span.start.pointSpan());
388 }
389
390 /// Parses the productions:
391 ///
392 /// indentless_sequence ::= (BLOCK-ENTRY block_node?)+
393 /// *********** *
394 Event _parseIndentlessSequenceEntry() {
395 var token = _scanner.peek();
396
397 if (token.type != TokenType.BLOCK_ENTRY) {
398 _state = _states.removeLast();
399 return new Event(EventType.SEQUENCE_END, token.span.start.pointSpan());
400 }
401
402 var start = token.span.start;
403 _scanner.scan();
404 token = _scanner.peek();
405
406 if (token.type == TokenType.BLOCK_ENTRY ||
407 token.type == TokenType.KEY ||
408 token.type == TokenType.VALUE ||
409 token.type == TokenType.BLOCK_END) {
410 _state = _State.INDENTLESS_SEQUENCE_ENTRY;
411 return _processEmptyScalar(start);
412 } else {
413 _states.add(_State.INDENTLESS_SEQUENCE_ENTRY);
414 return _parseNode(block: true);
415 }
416 }
417
418 /// Parses the productions:
419 ///
420 /// block_mapping ::= BLOCK-MAPPING_START
421 /// *******************
422 /// ((KEY block_node_or_indentless_sequence?)?
423 /// *** *
424 /// (VALUE block_node_or_indentless_sequence?)?)*
425 ///
426 /// BLOCK-END
427 /// *********
428 Event _parseBlockMappingKey({bool first: false}) {
429 if (first) _scanner.scan();
430
431 var token = _scanner.peek();
432 if (token.type == TokenType.KEY) {
433 var start = token.span.start;
434 _scanner.scan();
435 token = _scanner.peek();
436
437 if (token.type == TokenType.KEY ||
438 token.type == TokenType.VALUE ||
439 token.type == TokenType.BLOCK_END) {
440 _state = _State.BLOCK_MAPPING_VALUE;
441 return _processEmptyScalar(start);
442 } else {
443 _states.add(_State.BLOCK_MAPPING_VALUE);
444 return _parseNode(block: true, indentlessSequence: true);
445 }
446 }
447
448 // libyaml doesn't allow empty keys without an explicit key indicator, but
449 // the spec does. See example 8.18:
450 // http://yaml.org/spec/1.2/spec.html#id2798896.
451 if (token.type == TokenType.VALUE) {
452 _state = _State.BLOCK_MAPPING_VALUE;
453 return _processEmptyScalar(token.span.start);
454 }
455
456 if (token.type == TokenType.BLOCK_END) {
457 _scanner.scan();
458 _state = _states.removeLast();
459 return new Event(EventType.MAPPING_END, token.span);
460 }
461
462 throw new YamlException("Expected a key while parsing a block mapping.",
463 token.span.start.pointSpan());
464 }
465
466 /// Parses the productions:
467 ///
468 /// block_mapping ::= BLOCK-MAPPING_START
469 ///
470 /// ((KEY block_node_or_indentless_sequence?)?
471 ///
472 /// (VALUE block_node_or_indentless_sequence?)?)*
473 /// ***** *
474 /// BLOCK-END
475 ///
476 Event _parseBlockMappingValue() {
477 var token = _scanner.peek();
478
479 if (token.type != TokenType.VALUE) {
480 _state = _State.BLOCK_MAPPING_KEY;
481 return _processEmptyScalar(token.span.start);
482 }
483
484 var start = token.span.start;
485 _scanner.scan();
486 token = _scanner.peek();
487 if (token.type == TokenType.KEY ||
488 token.type == TokenType.VALUE ||
489 token.type == TokenType.BLOCK_END) {
490 _state = _State.BLOCK_MAPPING_KEY;
491 return _processEmptyScalar(start);
492 } else {
493 _states.add(_State.BLOCK_MAPPING_KEY);
494 return _parseNode(block: true, indentlessSequence: true);
495 }
496 }
497
498 /// Parses the productions:
499 ///
500 /// flow_sequence ::= FLOW-SEQUENCE-START
501 /// *******************
502 /// (flow_sequence_entry FLOW-ENTRY)*
503 /// * **********
504 /// flow_sequence_entry?
505 /// *
506 /// FLOW-SEQUENCE-END
507 /// *****************
508 /// flow_sequence_entry ::=
509 /// flow_node | KEY flow_node? (VALUE flow_node?)?
510 /// *
511 Event _parseFlowSequenceEntry({bool first: false}) {
512 if (first) _scanner.scan();
513 var token = _scanner.peek();
514
Bob Nystrom 2014/10/31 20:03:27 Extra blank line.
nweiz 2014/11/04 22:19:36 Done.
515
516 if (token.type != TokenType.FLOW_SEQUENCE_END) {
517 if (!first) {
518 if (token.type != TokenType.FLOW_ENTRY) {
519 throw new YamlException(
520 "While parsing a flow sequence, expected ',' or ']'.",
521 token.span.start.pointSpan());
522 }
523
524 _scanner.scan();
525 token = _scanner.peek();
526 }
527
528 if (token.type == TokenType.KEY) {
529 _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_KEY;
530 _scanner.scan();
531 return new MappingStartEvent(
532 token.span, CollectionStyle.FLOW);
533 } else if (token.type != TokenType.FLOW_SEQUENCE_END) {
534 _states.add(_State.FLOW_SEQUENCE_ENTRY);
535 return _parseNode();
536 }
537 }
538
539 _scanner.scan();
540 _state = _states.removeLast();
541 return new Event(EventType.SEQUENCE_END, token.span);
542 }
543
544 /// Parses the productions:
545 ///
546 /// flow_sequence_entry ::=
547 /// flow_node | KEY flow_node? (VALUE flow_node?)?
548 /// *** *
549 Event _parseFlowSequenceEntryMappingKey() {
550 var token = _scanner.peek();
551
552 if (token.type == TokenType.VALUE ||
553 token.type == TokenType.FLOW_ENTRY ||
554 token.type == TokenType.FLOW_SEQUENCE_END) {
555 // libyaml consumes the token here, but that seems like a bug, since it
556 // always causes [_parseFlowSequenceEntryMappingValue] to emit an empty
557 // scalar.
558
559 var start = token.span.start;
560 _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE;
561 return _processEmptyScalar(start);
562 } else {
563 _states.add(_State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE);
564 return _parseNode();
565 }
566 }
567
568 /// Parses the productions:
569 ///
570 /// flow_sequence_entry ::=
571 /// flow_node | KEY flow_node? (VALUE flow_node?)?
572 /// ***** *
573 Event _parseFlowSequenceEntryMappingValue() {
574 var token = _scanner.peek();
575
576 if (token.type == TokenType.VALUE) {
577 _scanner.scan();
578 token = _scanner.peek();
579 if (token.type != TokenType.FLOW_ENTRY &&
580 token.type != TokenType.FLOW_SEQUENCE_END) {
581 _states.add(_State.FLOW_SEQUENCE_ENTRY_MAPPING_END);
582 return _parseNode();
583 }
584 }
585
586 _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_END;
587 return _processEmptyScalar(token.span.start);
588 }
589
590 /// Parses the productions:
591 ///
592 /// flow_sequence_entry ::=
593 /// flow_node | KEY flow_node? (VALUE flow_node?)?
594 /// *
595 Event _parseFlowSequenceEntryMappingEnd() {
596 _state = _State.FLOW_SEQUENCE_ENTRY;
597 return new Event(EventType.MAPPING_END,
598 _scanner.peek().span.start.pointSpan());
599 }
600
601 /// Parses the productions:
602 ///
603 /// flow_mapping ::= FLOW-MAPPING-START
604 /// ******************
605 /// (flow_mapping_entry FLOW-ENTRY)*
606 /// * **********
607 /// flow_mapping_entry?
608 /// ******************
609 /// FLOW-MAPPING-END
610 /// ****************
611 /// flow_mapping_entry ::=
612 /// flow_node | KEY flow_node? (VALUE flow_node?)?
613 /// * *** *
614 Event _parseFlowMappingKey({bool first: false}) {
615 if (first) _scanner.scan();
616 var token = _scanner.peek();
617
618 if (token.type != TokenType.FLOW_MAPPING_END) {
619 if (!first) {
620 if (token.type != TokenType.FLOW_ENTRY) {
621 throw new YamlException(
622 "While parsing a flow mapping, expected ',' or '}'.",
623 token.span.start.pointSpan());
624 }
625
626 _scanner.scan();
627 token = _scanner.peek();
628 }
629
630 if (token.type == TokenType.KEY) {
631 _scanner.scan();
632 token = _scanner.peek();
633 if (token.type != TokenType.VALUE &&
634 token.type != TokenType.FLOW_ENTRY &&
635 token.type != TokenType.FLOW_MAPPING_END) {
636 _states.add(_State.FLOW_MAPPING_VALUE);
637 return _parseNode();
638 } else {
639 _state = _State.FLOW_MAPPING_VALUE;
640 return _processEmptyScalar(token.span.start);
641 }
642 } else if (token.type != TokenType.FLOW_MAPPING_END) {
643 _states.add(_State.FLOW_MAPPING_EMPTY_VALUE);
644 return _parseNode();
645 }
646 }
647
648 _scanner.scan();
649 _state = _states.removeLast();
650 return new Event(EventType.MAPPING_END, token.span);
651 }
652
653 /// Parses the productions:
654 ///
655 /// flow_mapping_entry ::=
656 /// flow_node | KEY flow_node? (VALUE flow_node?)?
657 /// * ***** *
658 Event _parseFlowMappingValue({bool empty: false}) {
659 var token = _scanner.peek();
660
661 if (empty) {
662 _state = _State.FLOW_MAPPING_KEY;
663 return _processEmptyScalar(token.span.start);
664 }
665
666 if (token.type == TokenType.VALUE) {
667 _scanner.scan();
668 token = _scanner.peek();
669 if (token.type != TokenType.FLOW_ENTRY &&
670 token.type != TokenType.FLOW_MAPPING_END) {
671 _states.add(_State.FLOW_MAPPING_KEY);
672 return _parseNode();
673 }
674 }
675
676 _state = _State.FLOW_MAPPING_KEY;
677 return _processEmptyScalar(token.span.start);
678 }
679
680 /// Generate an empty scalar event.
681 Event _processEmptyScalar(SourceLocation location) =>
682 new ScalarEvent(location.pointSpan(), '', ScalarStyle.PLAIN);
683
684 /// Parses directives.
685 Pair<VersionDirective, List<TagDirective>> _processDirectives() {
686 var token = _scanner.peek();
687
688 var versionDirective;
689 var tagDirectives = [];
690 var reservedDirectives = [];
691 while (token.type == TokenType.VERSION_DIRECTIVE ||
692 token.type == TokenType.TAG_DIRECTIVE) {
693 if (token is VersionDirectiveToken) {
694 if (versionDirective != null) {
695 throw new YamlException("Duplicate %YAML directive.", token.span);
696 }
697
698 if (token.major != 1 || token.minor == 0) {
699 throw new YamlException(
700 "Incompatible YAML document. This parser only supports YAML 1.1 "
701 "and 1.2.",
702 token.span);
703 } else if (token.minor > 2) {
704 // TODO(nweiz): Print to stderr when issue 6943 is fixed and dart:io
705 // is available.
706 warn("Warning: this parser only supports YAML 1.1 and 1.2.",
707 token.span);
708 }
709
710 versionDirective = new VersionDirective(token.major, token.minor);
711 } else if (token is TagDirectiveToken) {
712 var tagDirective = new TagDirective(token.handle, token.prefix);
713 _appendTagDirective(tagDirective, token.span);
714 tagDirectives.add(tagDirective);
715 }
716
717 _scanner.scan();
718 token = _scanner.peek();
719 }
720
721 _appendTagDirective(
722 new TagDirective("!", "!"),
723 token.span.start.pointSpan(),
724 allowDuplicates: true);
725 _appendTagDirective(
726 new TagDirective("!!", "tag:yaml.org,2002:"),
727 token.span.start.pointSpan(),
728 allowDuplicates: true);
729
730 return new Pair(versionDirective, tagDirectives);
731 }
732
733 /// Adds a tag directive to the directives stack.
734 void _appendTagDirective(TagDirective newDirective, FileSpan span,
735 {bool allowDuplicates: false}) {
736 if (_tagDirectives.containsKey(newDirective.handle)) {
737 if (allowDuplicates) return;
738 throw new YamlException("Duplicate %TAG directive.", span);
739 }
740
741 _tagDirectives[newDirective.handle] = newDirective;
1848 } 742 }
1849 } 743 }
1850 744
1851 /// The information in the header for a block scalar. 745 /// The possible states for the parser.
1852 class _BlockHeader { 746 class _State {
1853 final int additionalIndent; 747 /// Expect [TokenType.STREAM_START].
1854 final int chomping; 748 static const STREAM_START = const _State("STREAM_START");
1855 749
1856 _BlockHeader(this.additionalIndent, this.chomping); 750 /// Expect the beginning of an implicit document.
1857 751 static const IMPLICIT_DOCUMENT_START =
1858 bool get autoDetectIndent => additionalIndent == null; 752 const _State("IMPLICIT_DOCUMENT_START");
753
754 /// Expect [TokenType.DOCUMENT_START].
755 static const DOCUMENT_START = const _State("DOCUMENT_START");
756
757 /// Expect the content of a document.
758 static const DOCUMENT_CONTENT = const _State("DOCUMENT_CONTENT");
759
760 /// Expect [TokenType.DOCUMENT_END].
761 static const DOCUMENT_END = const _State("DOCUMENT_END");
762
763 /// Expect a block node.
764 static const BLOCK_NODE = const _State("BLOCK_NODE");
765
766 /// Expect a block node or indentless sequence.
767 static const BLOCK_NODE_OR_INDENTLESS_SEQUENCE =
768 const _State("BLOCK_NODE_OR_INDENTLESS_SEQUENCE");
769
770 /// Expect a flow node.
771 static const FLOW_NODE = const _State("FLOW_NODE");
772
773 /// Expect the first entry of a block sequence.
774 static const BLOCK_SEQUENCE_FIRST_ENTRY =
775 const _State("BLOCK_SEQUENCE_FIRST_ENTRY");
776
777 /// Expect an entry of a block sequence.
778 static const BLOCK_SEQUENCE_ENTRY = const _State("BLOCK_SEQUENCE_ENTRY");
779
780 /// Expect an entry of an indentless sequence.
781 static const INDENTLESS_SEQUENCE_ENTRY =
782 const _State("INDENTLESS_SEQUENCE_ENTRY");
783
784 /// Expect the first key of a block mapping.
785 static const BLOCK_MAPPING_FIRST_KEY =
786 const _State("BLOCK_MAPPING_FIRST_KEY");
787
788 /// Expect a block mapping key.
789 static const BLOCK_MAPPING_KEY = const _State("BLOCK_MAPPING_KEY");
790
791 /// Expect a block mapping value.
792 static const BLOCK_MAPPING_VALUE = const _State("BLOCK_MAPPING_VALUE");
793
794 /// Expect the first entry of a flow sequence.
795 static const FLOW_SEQUENCE_FIRST_ENTRY =
796 const _State("FLOW_SEQUENCE_FIRST_ENTRY");
797
798 /// Expect an entry of a flow sequence.
799 static const FLOW_SEQUENCE_ENTRY = const _State("FLOW_SEQUENCE_ENTRY");
800
801 /// Expect a key of an ordered mapping.
802 static const FLOW_SEQUENCE_ENTRY_MAPPING_KEY =
803 const _State("FLOW_SEQUENCE_ENTRY_MAPPING_KEY");
804
805 /// Expect a value of an ordered mapping.
806 static const FLOW_SEQUENCE_ENTRY_MAPPING_VALUE =
807 const _State("FLOW_SEQUENCE_ENTRY_MAPPING_VALUE");
808
809 /// Expect the and of an ordered mapping entry.
810 static const FLOW_SEQUENCE_ENTRY_MAPPING_END =
811 const _State("FLOW_SEQUENCE_ENTRY_MAPPING_END");
812
813 /// Expect the first key of a flow mapping.
814 static const FLOW_MAPPING_FIRST_KEY = const _State("FLOW_MAPPING_FIRST_KEY");
815
816 /// Expect a key of a flow mapping.
817 static const FLOW_MAPPING_KEY = const _State("FLOW_MAPPING_KEY");
818
819 /// Expect a value of a flow mapping.
820 static const FLOW_MAPPING_VALUE = const _State("FLOW_MAPPING_VALUE");
821
822 /// Expect an empty value of a flow mapping.
823 static const FLOW_MAPPING_EMPTY_VALUE =
824 const _State("FLOW_MAPPING_EMPTY_VALUE");
825
826 /// Expect nothing.
827 static const END = const _State("END");
828
829 final String name;
830
831 const _State(this.name);
832
833 String toString() => name;
1859 } 834 }
1860
1861 /// A range of characters in the YAML document, from [start] to [end]
1862 /// (inclusive).
1863 class _Range {
1864 /// The first character in the range.
1865 final int start;
1866
1867 /// The last character in the range.
1868 final int end;
1869
1870 _Range(this.start, this.end);
1871
1872 /// Returns whether or not [pos] lies within this range.
1873 bool contains(int pos) => pos >= start && pos <= end;
1874 }
1875
1876 /// A map that associates [E] values with [_Range]s. It's efficient to create
1877 /// new associations, but finding the value associated with a position is more
1878 /// expensive.
1879 class _RangeMap<E> {
1880 /// The ranges and their associated elements.
1881 final List<Pair<_Range, E>> _contents = <Pair<_Range, E>>[];
1882
1883 _RangeMap();
1884
1885 /// Returns the value associated with the range in which [pos] lies, or null
1886 /// if there is no such range. If there's more than one such range, the most
1887 /// recently set one is used.
1888 E operator[](int pos) {
1889 // Iterate backwards through contents so the more recent range takes
1890 // precedence.
1891 for (var pair in _contents.reversed) {
1892 if (pair.first.contains(pos)) return pair.last;
1893 }
1894 return null;
1895 }
1896
1897 /// Associates [value] with [range].
1898 operator[]=(_Range range, E value) =>
1899 _contents.add(new Pair<_Range, E>(range, value));
1900 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698