Rosetta
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
scanner.py
Go to the documentation of this file.
1 # (c) Copyright Rosetta Commons Member Institutions.
2 # (c) This file is part of the Rosetta software suite and is made available under license.
3 # (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
4 # (c) For more information, see http://www.rosettacommons.org. Questions about this can be
5 # (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
6 
7 # Scanner produces tokens of the following types:
8 # STREAM-START
9 # STREAM-END
10 # DIRECTIVE(name, value)
11 # DOCUMENT-START
12 # DOCUMENT-END
13 # BLOCK-SEQUENCE-START
14 # BLOCK-MAPPING-START
15 # BLOCK-END
16 # FLOW-SEQUENCE-START
17 # FLOW-MAPPING-START
18 # FLOW-SEQUENCE-END
19 # FLOW-MAPPING-END
20 # BLOCK-ENTRY
21 # FLOW-ENTRY
22 # KEY
23 # VALUE
24 # ALIAS(value)
25 # ANCHOR(value)
26 # TAG(value)
27 # SCALAR(value, plain, style)
28 #
29 # Read comments in the Scanner code for more details.
30 #
31 
32 __all__ = ['Scanner', 'ScannerError']
33 
34 from error import MarkedYAMLError
35 from tokens import *
36 
37 class ScannerError(MarkedYAMLError):
38  pass
39 
40 class SimpleKey(object):
41  # See below simple keys treatment.
42 
43  def __init__(self, token_number, required, index, line, column, mark):
44  self.token_number = token_number
45  self.required = required
46  self.index = index
47  self.line = line
48  self.column = column
49  self.mark = mark
50 
51 class Scanner(object):
52 
53  def __init__(self):
54  """Initialize the scanner."""
55  # It is assumed that Scanner and Reader will have a common descendant.
56  # Reader do the dirty work of checking for BOM and converting the
57  # input data to Unicode. It also adds NUL to the end.
58  #
59  # Reader supports the following methods
60  # self.peek(i=0) # peek the next i-th character
61  # self.prefix(l=1) # peek the next l characters
62  # self.forward(l=1) # read the next l characters and move the pointer.
63 
64  # Had we reached the end of the stream?
65  self.done = False
66 
67  # The number of unclosed '{' and '['. `flow_level == 0` means block
68  # context.
69  self.flow_level = 0
70 
71  # List of processed tokens that are not yet emitted.
72  self.tokens = []
73 
74  # Add the STREAM-START token.
75  self.fetch_stream_start()
76 
77  # Number of tokens that were emitted through the `get_token` method.
78  self.tokens_taken = 0
79 
80  # The current indentation level.
81  self.indent = -1
82 
83  # Past indentation levels.
84  self.indents = []
85 
86  # Variables related to simple keys treatment.
87 
88  # A simple key is a key that is not denoted by the '?' indicator.
89  # Example of simple keys:
90  # ---
91  # block simple key: value
92  # ? not a simple key:
93  # : { flow simple key: value }
94  # We emit the KEY token before all keys, so when we find a potential
95  # simple key, we try to locate the corresponding ':' indicator.
96  # Simple keys should be limited to a single line and 1024 characters.
97 
98  # Can a simple key start at the current position? A simple key may
99  # start:
100  # - at the beginning of the line, not counting indentation spaces
101  # (in block context),
102  # - after '{', '[', ',' (in the flow context),
103  # - after '?', ':', '-' (in the block context).
104  # In the block context, this flag also signifies if a block collection
105  # may start at the current position.
106  self.allow_simple_key = True
107 
108  # Keep track of possible simple keys. This is a dictionary. The key
109  # is `flow_level`; there can be no more that one possible simple key
110  # for each level. The value is a SimpleKey record:
111  # (token_number, required, index, line, column, mark)
112  # A simple key may start with ALIAS, ANCHOR, TAG, SCALAR(flow),
113  # '[', or '{' tokens.
115 
116  # Public methods.
117 
118  def check_token(self, *choices):
119  # Check if the next token is one of the given types.
120  while self.need_more_tokens():
121  self.fetch_more_tokens()
122  if self.tokens:
123  if not choices:
124  return True
125  for choice in choices:
126  if isinstance(self.tokens[0], choice):
127  return True
128  return False
129 
130  def peek_token(self):
131  # Return the next token, but do not delete if from the queue.
132  while self.need_more_tokens():
133  self.fetch_more_tokens()
134  if self.tokens:
135  return self.tokens[0]
136 
137  def get_token(self):
138  # Return the next token.
139  while self.need_more_tokens():
140  self.fetch_more_tokens()
141  if self.tokens:
142  self.tokens_taken += 1
143  return self.tokens.pop(0)
144 
145  # Private methods.
146 
147  def need_more_tokens(self):
148  if self.done:
149  return False
150  if not self.tokens:
151  return True
152  # The current token may be a potential simple key, so we
153  # need to look further.
155  if self.next_possible_simple_key() == self.tokens_taken:
156  return True
157 
158  def fetch_more_tokens(self):
159 
160  # Eat whitespaces and comments until we reach the next token.
161  self.scan_to_next_token()
162 
163  # Remove obsolete possible simple keys.
165 
166  # Compare the current indentation and column. It may add some tokens
167  # and decrease the current indentation level.
168  self.unwind_indent(self.column)
169 
170  # Peek the next character.
171  ch = self.peek()
172 
173  # Is it the end of stream?
174  if ch == u'\0':
175  return self.fetch_stream_end()
176 
177  # Is it a directive?
178  if ch == u'%' and self.check_directive():
179  return self.fetch_directive()
180 
181  # Is it the document start?
182  if ch == u'-' and self.check_document_start():
183  return self.fetch_document_start()
184 
185  # Is it the document end?
186  if ch == u'.' and self.check_document_end():
187  return self.fetch_document_end()
188 
189  # TODO: support for BOM within a stream.
190  #if ch == u'\uFEFF':
191  # return self.fetch_bom() <-- issue BOMToken
192 
193  # Note: the order of the following checks is NOT significant.
194 
195  # Is it the flow sequence start indicator?
196  if ch == u'[':
197  return self.fetch_flow_sequence_start()
198 
199  # Is it the flow mapping start indicator?
200  if ch == u'{':
201  return self.fetch_flow_mapping_start()
202 
203  # Is it the flow sequence end indicator?
204  if ch == u']':
205  return self.fetch_flow_sequence_end()
206 
207  # Is it the flow mapping end indicator?
208  if ch == u'}':
209  return self.fetch_flow_mapping_end()
210 
211  # Is it the flow entry indicator?
212  if ch == u',':
213  return self.fetch_flow_entry()
214 
215  # Is it the block entry indicator?
216  if ch == u'-' and self.check_block_entry():
217  return self.fetch_block_entry()
218 
219  # Is it the key indicator?
220  if ch == u'?' and self.check_key():
221  return self.fetch_key()
222 
223  # Is it the value indicator?
224  if ch == u':' and self.check_value():
225  return self.fetch_value()
226 
227  # Is it an alias?
228  if ch == u'*':
229  return self.fetch_alias()
230 
231  # Is it an anchor?
232  if ch == u'&':
233  return self.fetch_anchor()
234 
235  # Is it a tag?
236  if ch == u'!':
237  return self.fetch_tag()
238 
239  # Is it a literal scalar?
240  if ch == u'|' and not self.flow_level:
241  return self.fetch_literal()
242 
243  # Is it a folded scalar?
244  if ch == u'>' and not self.flow_level:
245  return self.fetch_folded()
246 
247  # Is it a single quoted scalar?
248  if ch == u'\'':
249  return self.fetch_single()
250 
251  # Is it a double quoted scalar?
252  if ch == u'\"':
253  return self.fetch_double()
254 
255  # It must be a plain scalar then.
256  if self.check_plain():
257  return self.fetch_plain()
258 
259  # No? It's an error. Let's produce a nice error message.
260  raise ScannerError("while scanning for the next token", None,
261  "found character %r that cannot start any token"
262  % ch.encode('utf-8'), self.get_mark())
263 
264  # Simple keys treatment.
265 
267  # Return the number of the nearest possible simple key. Actually we
268  # don't need to loop through the whole dictionary. We may replace it
269  # with the following code:
270  # if not self.possible_simple_keys:
271  # return None
272  # return self.possible_simple_keys[
273  # min(self.possible_simple_keys.keys())].token_number
274  min_token_number = None
275  for level in self.possible_simple_keys:
276  key = self.possible_simple_keys[level]
277  if min_token_number is None or key.token_number < min_token_number:
278  min_token_number = key.token_number
279  return min_token_number
280 
282  # Remove entries that are no longer possible simple keys. According to
283  # the YAML specification, simple keys
284  # - should be limited to a single line,
285  # - should be no longer than 1024 characters.
286  # Disabling this procedure will allow simple keys of any length and
287  # height (may cause problems if indentation is broken though).
288  for level in self.possible_simple_keys.keys():
289  key = self.possible_simple_keys[level]
290  if key.line != self.line \
291  or self.index-key.index > 1024:
292  if key.required:
293  raise ScannerError("while scanning a simple key", key.mark,
294  "could not found expected ':'", self.get_mark())
295  del self.possible_simple_keys[level]
296 
298  # The next token may start a simple key. We check if it's possible
299  # and save its position. This function is called for
300  # ALIAS, ANCHOR, TAG, SCALAR(flow), '[', and '{'.
301 
302  # Check if a simple key is required at the current position.
303  required = not self.flow_level and self.indent == self.column
304 
305  # A simple key is required only if it is the first token in the current
306  # line. Therefore it is always allowed.
307  assert self.allow_simple_key or not required
308 
309  # The next token might be a simple key. Let's save it's number and
310  # position.
311  if self.allow_simple_key:
313  token_number = self.tokens_taken+len(self.tokens)
314  key = SimpleKey(token_number, required,
315  self.index, self.line, self.column, self.get_mark())
316  self.possible_simple_keys[self.flow_level] = key
317 
319  # Remove the saved possible key position at the current flow level.
320  if self.flow_level in self.possible_simple_keys:
321  key = self.possible_simple_keys[self.flow_level]
322 
323  if key.required:
324  raise ScannerError("while scanning a simple key", key.mark,
325  "could not found expected ':'", self.get_mark())
326 
327  del self.possible_simple_keys[self.flow_level]
328 
329  # Indentation functions.
330 
331  def unwind_indent(self, column):
332 
333  ## In flow context, tokens should respect indentation.
334  ## Actually the condition should be `self.indent >= column` according to
335  ## the spec. But this condition will prohibit intuitively correct
336  ## constructions such as
337  ## key : {
338  ## }
339  #if self.flow_level and self.indent > column:
340  # raise ScannerError(None, None,
341  # "invalid intendation or unclosed '[' or '{'",
342  # self.get_mark())
343 
344  # In the flow context, indentation is ignored. We make the scanner less
345  # restrictive then specification requires.
346  if self.flow_level:
347  return
348 
349  # In block context, we may need to issue the BLOCK-END tokens.
350  while self.indent > column:
351  mark = self.get_mark()
352  self.indent = self.indents.pop()
353  self.tokens.append(BlockEndToken(mark, mark))
354 
355  def add_indent(self, column):
356  # Check if we need to increase indentation.
357  if self.indent < column:
358  self.indents.append(self.indent)
359  self.indent = column
360  return True
361  return False
362 
363  # Fetchers.
364 
366  # We always add STREAM-START as the first token and STREAM-END as the
367  # last token.
368 
369  # Read the token.
370  mark = self.get_mark()
371 
372  # Add STREAM-START.
373  self.tokens.append(StreamStartToken(mark, mark,
374  encoding=self.encoding))
375 
376 
377  def fetch_stream_end(self):
378 
379  # Set the current intendation to -1.
380  self.unwind_indent(-1)
381 
382  # Reset everything (not really needed).
383  self.allow_simple_key = False
384  self.possible_simple_keys = {}
385 
386  # Read the token.
387  mark = self.get_mark()
388 
389  # Add STREAM-END.
390  self.tokens.append(StreamEndToken(mark, mark))
391 
392  # The steam is finished.
393  self.done = True
394 
395  def fetch_directive(self):
396 
397  # Set the current intendation to -1.
398  self.unwind_indent(-1)
399 
400  # Reset simple keys.
402  self.allow_simple_key = False
403 
404  # Scan and add DIRECTIVE.
405  self.tokens.append(self.scan_directive())
406 
408  self.fetch_document_indicator(DocumentStartToken)
409 
411  self.fetch_document_indicator(DocumentEndToken)
412 
413  def fetch_document_indicator(self, TokenClass):
414 
415  # Set the current intendation to -1.
416  self.unwind_indent(-1)
417 
418  # Reset simple keys. Note that there could not be a block collection
419  # after '---'.
421  self.allow_simple_key = False
422 
423  # Add DOCUMENT-START or DOCUMENT-END.
424  start_mark = self.get_mark()
425  self.forward(3)
426  end_mark = self.get_mark()
427  self.tokens.append(TokenClass(start_mark, end_mark))
428 
430  self.fetch_flow_collection_start(FlowSequenceStartToken)
431 
433  self.fetch_flow_collection_start(FlowMappingStartToken)
434 
435  def fetch_flow_collection_start(self, TokenClass):
436 
437  # '[' and '{' may start a simple key.
439 
440  # Increase the flow level.
441  self.flow_level += 1
442 
443  # Simple keys are allowed after '[' and '{'.
444  self.allow_simple_key = True
445 
446  # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START.
447  start_mark = self.get_mark()
448  self.forward()
449  end_mark = self.get_mark()
450  self.tokens.append(TokenClass(start_mark, end_mark))
451 
453  self.fetch_flow_collection_end(FlowSequenceEndToken)
454 
456  self.fetch_flow_collection_end(FlowMappingEndToken)
457 
458  def fetch_flow_collection_end(self, TokenClass):
459 
460  # Reset possible simple key on the current level.
462 
463  # Decrease the flow level.
464  self.flow_level -= 1
465 
466  # No simple keys after ']' or '}'.
467  self.allow_simple_key = False
468 
469  # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END.
470  start_mark = self.get_mark()
471  self.forward()
472  end_mark = self.get_mark()
473  self.tokens.append(TokenClass(start_mark, end_mark))
474 
475  def fetch_flow_entry(self):
476 
477  # Simple keys are allowed after ','.
478  self.allow_simple_key = True
479 
480  # Reset possible simple key on the current level.
482 
483  # Add FLOW-ENTRY.
484  start_mark = self.get_mark()
485  self.forward()
486  end_mark = self.get_mark()
487  self.tokens.append(FlowEntryToken(start_mark, end_mark))
488 
489  def fetch_block_entry(self):
490 
491  # Block context needs additional checks.
492  if not self.flow_level:
493 
494  # Are we allowed to start a new entry?
495  if not self.allow_simple_key:
496  raise ScannerError(None, None,
497  "sequence entries are not allowed here",
498  self.get_mark())
499 
500  # We may need to add BLOCK-SEQUENCE-START.
501  if self.add_indent(self.column):
502  mark = self.get_mark()
503  self.tokens.append(BlockSequenceStartToken(mark, mark))
504 
505  # It's an error for the block entry to occur in the flow context,
506  # but we let the parser detect this.
507  else:
508  pass
509 
510  # Simple keys are allowed after '-'.
511  self.allow_simple_key = True
512 
513  # Reset possible simple key on the current level.
515 
516  # Add BLOCK-ENTRY.
517  start_mark = self.get_mark()
518  self.forward()
519  end_mark = self.get_mark()
520  self.tokens.append(BlockEntryToken(start_mark, end_mark))
521 
522  def fetch_key(self):
523 
524  # Block context needs additional checks.
525  if not self.flow_level:
526 
527  # Are we allowed to start a key (not nessesary a simple)?
528  if not self.allow_simple_key:
529  raise ScannerError(None, None,
530  "mapping keys are not allowed here",
531  self.get_mark())
532 
533  # We may need to add BLOCK-MAPPING-START.
534  if self.add_indent(self.column):
535  mark = self.get_mark()
536  self.tokens.append(BlockMappingStartToken(mark, mark))
537 
538  # Simple keys are allowed after '?' in the block context.
539  self.allow_simple_key = not self.flow_level
540 
541  # Reset possible simple key on the current level.
543 
544  # Add KEY.
545  start_mark = self.get_mark()
546  self.forward()
547  end_mark = self.get_mark()
548  self.tokens.append(KeyToken(start_mark, end_mark))
549 
550  def fetch_value(self):
551 
552  # Do we determine a simple key?
553  if self.flow_level in self.possible_simple_keys:
554 
555  # Add KEY.
556  key = self.possible_simple_keys[self.flow_level]
557  del self.possible_simple_keys[self.flow_level]
558  self.tokens.insert(key.token_number-self.tokens_taken,
559  KeyToken(key.mark, key.mark))
560 
561  # If this key starts a new block mapping, we need to add
562  # BLOCK-MAPPING-START.
563  if not self.flow_level:
564  if self.add_indent(key.column):
565  self.tokens.insert(key.token_number-self.tokens_taken,
566  BlockMappingStartToken(key.mark, key.mark))
567 
568  # There cannot be two simple keys one after another.
569  self.allow_simple_key = False
570 
571  # It must be a part of a complex key.
572  else:
573 
574  # Block context needs additional checks.
575  # (Do we really need them? They will be catched by the parser
576  # anyway.)
577  if not self.flow_level:
578 
579  # We are allowed to start a complex value if and only if
580  # we can start a simple key.
581  if not self.allow_simple_key:
582  raise ScannerError(None, None,
583  "mapping values are not allowed here",
584  self.get_mark())
585 
586  # If this value starts a new block mapping, we need to add
587  # BLOCK-MAPPING-START. It will be detected as an error later by
588  # the parser.
589  if not self.flow_level:
590  if self.add_indent(self.column):
591  mark = self.get_mark()
592  self.tokens.append(BlockMappingStartToken(mark, mark))
593 
594  # Simple keys are allowed after ':' in the block context.
595  self.allow_simple_key = not self.flow_level
596 
597  # Reset possible simple key on the current level.
599 
600  # Add VALUE.
601  start_mark = self.get_mark()
602  self.forward()
603  end_mark = self.get_mark()
604  self.tokens.append(ValueToken(start_mark, end_mark))
605 
606  def fetch_alias(self):
607 
608  # ALIAS could be a simple key.
610 
611  # No simple keys after ALIAS.
612  self.allow_simple_key = False
613 
614  # Scan and add ALIAS.
615  self.tokens.append(self.scan_anchor(AliasToken))
616 
617  def fetch_anchor(self):
618 
619  # ANCHOR could start a simple key.
621 
622  # No simple keys after ANCHOR.
623  self.allow_simple_key = False
624 
625  # Scan and add ANCHOR.
626  self.tokens.append(self.scan_anchor(AnchorToken))
627 
628  def fetch_tag(self):
629 
630  # TAG could start a simple key.
632 
633  # No simple keys after TAG.
634  self.allow_simple_key = False
635 
636  # Scan and add TAG.
637  self.tokens.append(self.scan_tag())
638 
639  def fetch_literal(self):
640  self.fetch_block_scalar(style='|')
641 
642  def fetch_folded(self):
643  self.fetch_block_scalar(style='>')
644 
645  def fetch_block_scalar(self, style):
646 
647  # A simple key may follow a block scalar.
648  self.allow_simple_key = True
649 
650  # Reset possible simple key on the current level.
652 
653  # Scan and add SCALAR.
654  self.tokens.append(self.scan_block_scalar(style))
655 
656  def fetch_single(self):
657  self.fetch_flow_scalar(style='\'')
658 
659  def fetch_double(self):
660  self.fetch_flow_scalar(style='"')
661 
662  def fetch_flow_scalar(self, style):
663 
664  # A flow scalar could be a simple key.
666 
667  # No simple keys after flow scalars.
668  self.allow_simple_key = False
669 
670  # Scan and add SCALAR.
671  self.tokens.append(self.scan_flow_scalar(style))
672 
673  def fetch_plain(self):
674 
675  # A plain scalar could be a simple key.
677 
678  # No simple keys after plain scalars. But note that `scan_plain` will
679  # change this flag if the scan is finished at the beginning of the
680  # line.
681  self.allow_simple_key = False
682 
683  # Scan and add SCALAR. May change `allow_simple_key`.
684  self.tokens.append(self.scan_plain())
685 
686  # Checkers.
687 
688  def check_directive(self):
689 
690  # DIRECTIVE: ^ '%' ...
691  # The '%' indicator is already checked.
692  if self.column == 0:
693  return True
694 
696 
697  # DOCUMENT-START: ^ '---' (' '|'\n')
698  if self.column == 0:
699  if self.prefix(3) == u'---' \
700  and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029':
701  return True
702 
704 
705  # DOCUMENT-END: ^ '...' (' '|'\n')
706  if self.column == 0:
707  if self.prefix(3) == u'...' \
708  and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029':
709  return True
710 
711  def check_block_entry(self):
712 
713  # BLOCK-ENTRY: '-' (' '|'\n')
714  return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029'
715 
716  def check_key(self):
717 
718  # KEY(flow context): '?'
719  if self.flow_level:
720  return True
721 
722  # KEY(block context): '?' (' '|'\n')
723  else:
724  return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029'
725 
726  def check_value(self):
727 
728  # VALUE(flow context): ':'
729  if self.flow_level:
730  return True
731 
732  # VALUE(block context): ':' (' '|'\n')
733  else:
734  return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029'
735 
736  def check_plain(self):
737 
738  # A plain scalar may start with any non-space character except:
739  # '-', '?', ':', ',', '[', ']', '{', '}',
740  # '#', '&', '*', '!', '|', '>', '\'', '\"',
741  # '%', '@', '`'.
742  #
743  # It may also start with
744  # '-', '?', ':'
745  # if it is followed by a non-space character.
746  #
747  # Note that we limit the last rule to the block context (except the
748  # '-' character) because we want the flow context to be space
749  # independent.
750  ch = self.peek()
751  return ch not in u'\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' \
752  or (self.peek(1) not in u'\0 \t\r\n\x85\u2028\u2029'
753  and (ch == u'-' or (not self.flow_level and ch in u'?:')))
754 
755  # Scanners.
756 
758  # We ignore spaces, line breaks and comments.
759  # If we find a line break in the block context, we set the flag
760  # `allow_simple_key` on.
761  # The byte order mark is stripped if it's the first character in the
762  # stream. We do not yet support BOM inside the stream as the
763  # specification requires. Any such mark will be considered as a part
764  # of the document.
765  #
766  # TODO: We need to make tab handling rules more sane. A good rule is
767  # Tabs cannot precede tokens
768  # BLOCK-SEQUENCE-START, BLOCK-MAPPING-START, BLOCK-END,
769  # KEY(block), VALUE(block), BLOCK-ENTRY
770  # So the checking code is
771  # if <TAB>:
772  # self.allow_simple_keys = False
773  # We also need to add the check for `allow_simple_keys == True` to
774  # `unwind_indent` before issuing BLOCK-END.
775  # Scanners for block, flow, and plain scalars need to be modified.
776 
777  if self.index == 0 and self.peek() == u'\uFEFF':
778  self.forward()
779  found = False
780  while not found:
781  while self.peek() == u' ':
782  self.forward()
783  if self.peek() == u'#':
784  while self.peek() not in u'\0\r\n\x85\u2028\u2029':
785  self.forward()
786  if self.scan_line_break():
787  if not self.flow_level:
788  self.allow_simple_key = True
789  else:
790  found = True
791 
792  def scan_directive(self):
793  # See the specification for details.
794  start_mark = self.get_mark()
795  self.forward()
796  name = self.scan_directive_name(start_mark)
797  value = None
798  if name == u'YAML':
799  value = self.scan_yaml_directive_value(start_mark)
800  end_mark = self.get_mark()
801  elif name == u'TAG':
802  value = self.scan_tag_directive_value(start_mark)
803  end_mark = self.get_mark()
804  else:
805  end_mark = self.get_mark()
806  while self.peek() not in u'\0\r\n\x85\u2028\u2029':
807  self.forward()
808  self.scan_directive_ignored_line(start_mark)
809  return DirectiveToken(name, value, start_mark, end_mark)
810 
811  def scan_directive_name(self, start_mark):
812  # See the specification for details.
813  length = 0
814  ch = self.peek(length)
815  while u'0' <= ch <= u'9' or u'A' <= ch <= 'Z' or u'a' <= ch <= 'z' \
816  or ch in u'-_':
817  length += 1
818  ch = self.peek(length)
819  if not length:
820  raise ScannerError("while scanning a directive", start_mark,
821  "expected alphabetic or numeric character, but found %r"
822  % ch.encode('utf-8'), self.get_mark())
823  value = self.prefix(length)
824  self.forward(length)
825  ch = self.peek()
826  if ch not in u'\0 \r\n\x85\u2028\u2029':
827  raise ScannerError("while scanning a directive", start_mark,
828  "expected alphabetic or numeric character, but found %r"
829  % ch.encode('utf-8'), self.get_mark())
830  return value
831 
832  def scan_yaml_directive_value(self, start_mark):
833  # See the specification for details.
834  while self.peek() == u' ':
835  self.forward()
836  major = self.scan_yaml_directive_number(start_mark)
837  if self.peek() != '.':
838  raise ScannerError("while scanning a directive", start_mark,
839  "expected a digit or '.', but found %r"
840  % self.peek().encode('utf-8'),
841  self.get_mark())
842  self.forward()
843  minor = self.scan_yaml_directive_number(start_mark)
844  if self.peek() not in u'\0 \r\n\x85\u2028\u2029':
845  raise ScannerError("while scanning a directive", start_mark,
846  "expected a digit or ' ', but found %r"
847  % self.peek().encode('utf-8'),
848  self.get_mark())
849  return (major, minor)
850 
851  def scan_yaml_directive_number(self, start_mark):
852  # See the specification for details.
853  ch = self.peek()
854  if not (u'0' <= ch <= '9'):
855  raise ScannerError("while scanning a directive", start_mark,
856  "expected a digit, but found %r" % ch.encode('utf-8'),
857  self.get_mark())
858  length = 0
859  while u'0' <= self.peek(length) <= u'9':
860  length += 1
861  value = int(self.prefix(length))
862  self.forward(length)
863  return value
864 
865  def scan_tag_directive_value(self, start_mark):
866  # See the specification for details.
867  while self.peek() == u' ':
868  self.forward()
869  handle = self.scan_tag_directive_handle(start_mark)
870  while self.peek() == u' ':
871  self.forward()
872  prefix = self.scan_tag_directive_prefix(start_mark)
873  return (handle, prefix)
874 
875  def scan_tag_directive_handle(self, start_mark):
876  # See the specification for details.
877  value = self.scan_tag_handle('directive', start_mark)
878  ch = self.peek()
879  if ch != u' ':
880  raise ScannerError("while scanning a directive", start_mark,
881  "expected ' ', but found %r" % ch.encode('utf-8'),
882  self.get_mark())
883  return value
884 
885  def scan_tag_directive_prefix(self, start_mark):
886  # See the specification for details.
887  value = self.scan_tag_uri('directive', start_mark)
888  ch = self.peek()
889  if ch not in u'\0 \r\n\x85\u2028\u2029':
890  raise ScannerError("while scanning a directive", start_mark,
891  "expected ' ', but found %r" % ch.encode('utf-8'),
892  self.get_mark())
893  return value
894 
895  def scan_directive_ignored_line(self, start_mark):
896  # See the specification for details.
897  while self.peek() == u' ':
898  self.forward()
899  if self.peek() == u'#':
900  while self.peek() not in u'\0\r\n\x85\u2028\u2029':
901  self.forward()
902  ch = self.peek()
903  if ch not in u'\0\r\n\x85\u2028\u2029':
904  raise ScannerError("while scanning a directive", start_mark,
905  "expected a comment or a line break, but found %r"
906  % ch.encode('utf-8'), self.get_mark())
907  self.scan_line_break()
908 
909  def scan_anchor(self, TokenClass):
910  # The specification does not restrict characters for anchors and
911  # aliases. This may lead to problems, for instance, the document:
912  # [ *alias, value ]
913  # can be interpteted in two ways, as
914  # [ "value" ]
915  # and
916  # [ *alias , "value" ]
917  # Therefore we restrict aliases to numbers and ASCII letters.
918  start_mark = self.get_mark()
919  indicator = self.peek()
920  if indicator == '*':
921  name = 'alias'
922  else:
923  name = 'anchor'
924  self.forward()
925  length = 0
926  ch = self.peek(length)
927  while u'0' <= ch <= u'9' or u'A' <= ch <= 'Z' or u'a' <= ch <= 'z' \
928  or ch in u'-_':
929  length += 1
930  ch = self.peek(length)
931  if not length:
932  raise ScannerError("while scanning an %s" % name, start_mark,
933  "expected alphabetic or numeric character, but found %r"
934  % ch.encode('utf-8'), self.get_mark())
935  value = self.prefix(length)
936  self.forward(length)
937  ch = self.peek()
938  if ch not in u'\0 \t\r\n\x85\u2028\u2029?:,]}%@`':
939  raise ScannerError("while scanning an %s" % name, start_mark,
940  "expected alphabetic or numeric character, but found %r"
941  % ch.encode('utf-8'), self.get_mark())
942  end_mark = self.get_mark()
943  return TokenClass(value, start_mark, end_mark)
944 
945  def scan_tag(self):
946  # See the specification for details.
947  start_mark = self.get_mark()
948  ch = self.peek(1)
949  if ch == u'<':
950  handle = None
951  self.forward(2)
952  suffix = self.scan_tag_uri('tag', start_mark)
953  if self.peek() != u'>':
954  raise ScannerError("while parsing a tag", start_mark,
955  "expected '>', but found %r" % self.peek().encode('utf-8'),
956  self.get_mark())
957  self.forward()
958  elif ch in u'\0 \t\r\n\x85\u2028\u2029':
959  handle = None
960  suffix = u'!'
961  self.forward()
962  else:
963  length = 1
964  use_handle = False
965  while ch not in u'\0 \r\n\x85\u2028\u2029':
966  if ch == u'!':
967  use_handle = True
968  break
969  length += 1
970  ch = self.peek(length)
971  handle = u'!'
972  if use_handle:
973  handle = self.scan_tag_handle('tag', start_mark)
974  else:
975  handle = u'!'
976  self.forward()
977  suffix = self.scan_tag_uri('tag', start_mark)
978  ch = self.peek()
979  if ch not in u'\0 \r\n\x85\u2028\u2029':
980  raise ScannerError("while scanning a tag", start_mark,
981  "expected ' ', but found %r" % ch.encode('utf-8'),
982  self.get_mark())
983  value = (handle, suffix)
984  end_mark = self.get_mark()
985  return TagToken(value, start_mark, end_mark)
986 
987  def scan_block_scalar(self, style):
988  # See the specification for details.
989 
990  if style == '>':
991  folded = True
992  else:
993  folded = False
994 
995  chunks = []
996  start_mark = self.get_mark()
997 
998  # Scan the header.
999  self.forward()
1000  chomping, increment = self.scan_block_scalar_indicators(start_mark)
1001  self.scan_block_scalar_ignored_line(start_mark)
1002 
1003  # Determine the indentation level and go to the first non-empty line.
1004  min_indent = self.indent+1
1005  if min_indent < 1:
1006  min_indent = 1
1007  if increment is None:
1008  breaks, max_indent, end_mark = self.scan_block_scalar_indentation()
1009  indent = max(min_indent, max_indent)
1010  else:
1011  indent = min_indent+increment-1
1012  breaks, end_mark = self.scan_block_scalar_breaks(indent)
1013  line_break = u''
1014 
1015  # Scan the inner part of the block scalar.
1016  while self.column == indent and self.peek() != u'\0':
1017  chunks.extend(breaks)
1018  leading_non_space = self.peek() not in u' \t'
1019  length = 0
1020  while self.peek(length) not in u'\0\r\n\x85\u2028\u2029':
1021  length += 1
1022  chunks.append(self.prefix(length))
1023  self.forward(length)
1024  line_break = self.scan_line_break()
1025  breaks, end_mark = self.scan_block_scalar_breaks(indent)
1026  if self.column == indent and self.peek() != u'\0':
1027 
1028  # Unfortunately, folding rules are ambiguous.
1029  #
1030  # This is the folding according to the specification:
1031 
1032  if folded and line_break == u'\n' \
1033  and leading_non_space and self.peek() not in u' \t':
1034  if not breaks:
1035  chunks.append(u' ')
1036  else:
1037  chunks.append(line_break)
1038 
1039  # This is Clark Evans's interpretation (also in the spec
1040  # examples):
1041  #
1042  #if folded and line_break == u'\n':
1043  # if not breaks:
1044  # if self.peek() not in ' \t':
1045  # chunks.append(u' ')
1046  # else:
1047  # chunks.append(line_break)
1048  #else:
1049  # chunks.append(line_break)
1050  else:
1051  break
1052 
1053  # Chomp the tail.
1054  if chomping is not False:
1055  chunks.append(line_break)
1056  if chomping is True:
1057  chunks.extend(breaks)
1058 
1059  # We are done.
1060  return ScalarToken(u''.join(chunks), False, start_mark, end_mark,
1061  style)
1062 
1063  def scan_block_scalar_indicators(self, start_mark):
1064  # See the specification for details.
1065  chomping = None
1066  increment = None
1067  ch = self.peek()
1068  if ch in u'+-':
1069  if ch == '+':
1070  chomping = True
1071  else:
1072  chomping = False
1073  self.forward()
1074  ch = self.peek()
1075  if ch in u'0123456789':
1076  increment = int(ch)
1077  if increment == 0:
1078  raise ScannerError("while scanning a block scalar", start_mark,
1079  "expected indentation indicator in the range 1-9, but found 0",
1080  self.get_mark())
1081  self.forward()
1082  elif ch in u'0123456789':
1083  increment = int(ch)
1084  if increment == 0:
1085  raise ScannerError("while scanning a block scalar", start_mark,
1086  "expected indentation indicator in the range 1-9, but found 0",
1087  self.get_mark())
1088  self.forward()
1089  ch = self.peek()
1090  if ch in u'+-':
1091  if ch == '+':
1092  chomping = True
1093  else:
1094  chomping = False
1095  self.forward()
1096  ch = self.peek()
1097  if ch not in u'\0 \r\n\x85\u2028\u2029':
1098  raise ScannerError("while scanning a block scalar", start_mark,
1099  "expected chomping or indentation indicators, but found %r"
1100  % ch.encode('utf-8'), self.get_mark())
1101  return chomping, increment
1102 
1103  def scan_block_scalar_ignored_line(self, start_mark):
1104  # See the specification for details.
1105  while self.peek() == u' ':
1106  self.forward()
1107  if self.peek() == u'#':
1108  while self.peek() not in u'\0\r\n\x85\u2028\u2029':
1109  self.forward()
1110  ch = self.peek()
1111  if ch not in u'\0\r\n\x85\u2028\u2029':
1112  raise ScannerError("while scanning a block scalar", start_mark,
1113  "expected a comment or a line break, but found %r"
1114  % ch.encode('utf-8'), self.get_mark())
1115  self.scan_line_break()
1116 
1118  # See the specification for details.
1119  chunks = []
1120  max_indent = 0
1121  end_mark = self.get_mark()
1122  while self.peek() in u' \r\n\x85\u2028\u2029':
1123  if self.peek() != u' ':
1124  chunks.append(self.scan_line_break())
1125  end_mark = self.get_mark()
1126  else:
1127  self.forward()
1128  if self.column > max_indent:
1129  max_indent = self.column
1130  return chunks, max_indent, end_mark
1131 
1132  def scan_block_scalar_breaks(self, indent):
1133  # See the specification for details.
1134  chunks = []
1135  end_mark = self.get_mark()
1136  while self.column < indent and self.peek() == u' ':
1137  self.forward()
1138  while self.peek() in u'\r\n\x85\u2028\u2029':
1139  chunks.append(self.scan_line_break())
1140  end_mark = self.get_mark()
1141  while self.column < indent and self.peek() == u' ':
1142  self.forward()
1143  return chunks, end_mark
1144 
1145  def scan_flow_scalar(self, style):
1146  # See the specification for details.
1147  # Note that we loose indentation rules for quoted scalars. Quoted
1148  # scalars don't need to adhere indentation because " and ' clearly
1149  # mark the beginning and the end of them. Therefore we are less
1150  # restrictive then the specification requires. We only need to check
1151  # that document separators are not included in scalars.
1152  if style == '"':
1153  double = True
1154  else:
1155  double = False
1156  chunks = []
1157  start_mark = self.get_mark()
1158  quote = self.peek()
1159  self.forward()
1160  chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark))
1161  while self.peek() != quote:
1162  chunks.extend(self.scan_flow_scalar_spaces(double, start_mark))
1163  chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark))
1164  self.forward()
1165  end_mark = self.get_mark()
1166  return ScalarToken(u''.join(chunks), False, start_mark, end_mark,
1167  style)
1168 
1169  ESCAPE_REPLACEMENTS = {
1170  u'0': u'\0',
1171  u'a': u'\x07',
1172  u'b': u'\x08',
1173  u't': u'\x09',
1174  u'\t': u'\x09',
1175  u'n': u'\x0A',
1176  u'v': u'\x0B',
1177  u'f': u'\x0C',
1178  u'r': u'\x0D',
1179  u'e': u'\x1B',
1180  u' ': u'\x20',
1181  u'\"': u'\"',
1182  u'\\': u'\\',
1183  u'N': u'\x85',
1184  u'_': u'\xA0',
1185  u'L': u'\u2028',
1186  u'P': u'\u2029',
1187  }
1188 
1189  ESCAPE_CODES = {
1190  u'x': 2,
1191  u'u': 4,
1192  u'U': 8,
1193  }
1194 
1195  def scan_flow_scalar_non_spaces(self, double, start_mark):
1196  # See the specification for details.
1197  chunks = []
1198  while True:
1199  length = 0
1200  while self.peek(length) not in u'\'\"\\\0 \t\r\n\x85\u2028\u2029':
1201  length += 1
1202  if length:
1203  chunks.append(self.prefix(length))
1204  self.forward(length)
1205  ch = self.peek()
1206  if not double and ch == u'\'' and self.peek(1) == u'\'':
1207  chunks.append(u'\'')
1208  self.forward(2)
1209  elif (double and ch == u'\'') or (not double and ch in u'\"\\'):
1210  chunks.append(ch)
1211  self.forward()
1212  elif double and ch == u'\\':
1213  self.forward()
1214  ch = self.peek()
1215  if ch in self.ESCAPE_REPLACEMENTS:
1216  chunks.append(self.ESCAPE_REPLACEMENTS[ch])
1217  self.forward()
1218  elif ch in self.ESCAPE_CODES:
1219  length = self.ESCAPE_CODES[ch]
1220  self.forward()
1221  for k in range(length):
1222  if self.peek(k) not in u'0123456789ABCDEFabcdef':
1223  raise ScannerError("while scanning a double-quoted scalar", start_mark,
1224  "expected escape sequence of %d hexdecimal numbers, but found %r" %
1225  (length, self.peek(k).encode('utf-8')), self.get_mark())
1226  code = int(self.prefix(length), 16)
1227  chunks.append(unichr(code))
1228  self.forward(length)
1229  elif ch in u'\r\n\x85\u2028\u2029':
1230  self.scan_line_break()
1231  chunks.extend(self.scan_flow_scalar_breaks(double, start_mark))
1232  else:
1233  raise ScannerError("while scanning a double-quoted scalar", start_mark,
1234  "found unknown escape character %r" % ch.encode('utf-8'), self.get_mark())
1235  else:
1236  return chunks
1237 
1238  def scan_flow_scalar_spaces(self, double, start_mark):
1239  # See the specification for details.
1240  chunks = []
1241  length = 0
1242  while self.peek(length) in u' \t':
1243  length += 1
1244  whitespaces = self.prefix(length)
1245  self.forward(length)
1246  ch = self.peek()
1247  if ch == u'\0':
1248  raise ScannerError("while scanning a quoted scalar", start_mark,
1249  "found unexpected end of stream", self.get_mark())
1250  elif ch in u'\r\n\x85\u2028\u2029':
1251  line_break = self.scan_line_break()
1252  breaks = self.scan_flow_scalar_breaks(double, start_mark)
1253  if line_break != u'\n':
1254  chunks.append(line_break)
1255  elif not breaks:
1256  chunks.append(u' ')
1257  chunks.extend(breaks)
1258  else:
1259  chunks.append(whitespaces)
1260  return chunks
1261 
1262  def scan_flow_scalar_breaks(self, double, start_mark):
1263  # See the specification for details.
1264  chunks = []
1265  while True:
1266  # Instead of checking indentation, we check for document
1267  # separators.
1268  prefix = self.prefix(3)
1269  if (prefix == u'---' or prefix == u'...') \
1270  and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029':
1271  raise ScannerError("while scanning a quoted scalar", start_mark,
1272  "found unexpected document separator", self.get_mark())
1273  while self.peek() in u' \t':
1274  self.forward()
1275  if self.peek() in u'\r\n\x85\u2028\u2029':
1276  chunks.append(self.scan_line_break())
1277  else:
1278  return chunks
1279 
1280  def scan_plain(self):
1281  # See the specification for details.
1282  # We add an additional restriction for the flow context:
1283  # plain scalars in the flow context cannot contain ',', ':' and '?'.
1284  # We also keep track of the `allow_simple_key` flag here.
1285  # Indentation rules are loosed for the flow context.
1286  chunks = []
1287  start_mark = self.get_mark()
1288  end_mark = start_mark
1289  indent = self.indent+1
1290  # We allow zero indentation for scalars, but then we need to check for
1291  # document separators at the beginning of the line.
1292  #if indent == 0:
1293  # indent = 1
1294  spaces = []
1295  while True:
1296  length = 0
1297  if self.peek() == u'#':
1298  break
1299  while True:
1300  ch = self.peek(length)
1301  if ch in u'\0 \t\r\n\x85\u2028\u2029' \
1302  or (not self.flow_level and ch == u':' and
1303  self.peek(length+1) in u'\0 \t\r\n\x85\u2028\u2029') \
1304  or (self.flow_level and ch in u',:?[]{}'):
1305  break
1306  length += 1
1307  # It's not clear what we should do with ':' in the flow context.
1308  if (self.flow_level and ch == u':'
1309  and self.peek(length+1) not in u'\0 \t\r\n\x85\u2028\u2029,[]{}'):
1310  self.forward(length)
1311  raise ScannerError("while scanning a plain scalar", start_mark,
1312  "found unexpected ':'", self.get_mark(),
1313  "Please check http://pyyaml.org/wiki/YAMLColonInFlowContext for details.")
1314  if length == 0:
1315  break
1316  self.allow_simple_key = False
1317  chunks.extend(spaces)
1318  chunks.append(self.prefix(length))
1319  self.forward(length)
1320  end_mark = self.get_mark()
1321  spaces = self.scan_plain_spaces(indent, start_mark)
1322  if not spaces or self.peek() == u'#' \
1323  or (not self.flow_level and self.column < indent):
1324  break
1325  return ScalarToken(u''.join(chunks), True, start_mark, end_mark)
1326 
1327  def scan_plain_spaces(self, indent, start_mark):
1328  # See the specification for details.
1329  # The specification is really confusing about tabs in plain scalars.
1330  # We just forbid them completely. Do not use tabs in YAML!
1331  chunks = []
1332  length = 0
1333  while self.peek(length) in u' ':
1334  length += 1
1335  whitespaces = self.prefix(length)
1336  self.forward(length)
1337  ch = self.peek()
1338  if ch in u'\r\n\x85\u2028\u2029':
1339  line_break = self.scan_line_break()
1340  self.allow_simple_key = True
1341  prefix = self.prefix(3)
1342  if (prefix == u'---' or prefix == u'...') \
1343  and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029':
1344  return
1345  breaks = []
1346  while self.peek() in u' \r\n\x85\u2028\u2029':
1347  if self.peek() == ' ':
1348  self.forward()
1349  else:
1350  breaks.append(self.scan_line_break())
1351  prefix = self.prefix(3)
1352  if (prefix == u'---' or prefix == u'...') \
1353  and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029':
1354  return
1355  if line_break != u'\n':
1356  chunks.append(line_break)
1357  elif not breaks:
1358  chunks.append(u' ')
1359  chunks.extend(breaks)
1360  elif whitespaces:
1361  chunks.append(whitespaces)
1362  return chunks
1363 
1364  def scan_tag_handle(self, name, start_mark):
1365  # See the specification for details.
1366  # For some strange reasons, the specification does not allow '_' in
1367  # tag handles. I have allowed it anyway.
1368  ch = self.peek()
1369  if ch != u'!':
1370  raise ScannerError("while scanning a %s" % name, start_mark,
1371  "expected '!', but found %r" % ch.encode('utf-8'),
1372  self.get_mark())
1373  length = 1
1374  ch = self.peek(length)
1375  if ch != u' ':
1376  while u'0' <= ch <= u'9' or u'A' <= ch <= 'Z' or u'a' <= ch <= 'z' \
1377  or ch in u'-_':
1378  length += 1
1379  ch = self.peek(length)
1380  if ch != u'!':
1381  self.forward(length)
1382  raise ScannerError("while scanning a %s" % name, start_mark,
1383  "expected '!', but found %r" % ch.encode('utf-8'),
1384  self.get_mark())
1385  length += 1
1386  value = self.prefix(length)
1387  self.forward(length)
1388  return value
1389 
1390  def scan_tag_uri(self, name, start_mark):
1391  # See the specification for details.
1392  # Note: we do not check if URI is well-formed.
1393  chunks = []
1394  length = 0
1395  ch = self.peek(length)
1396  while u'0' <= ch <= u'9' or u'A' <= ch <= 'Z' or u'a' <= ch <= 'z' \
1397  or ch in u'-;/?:@&=+$,_.!~*\'()[]%':
1398  if ch == u'%':
1399  chunks.append(self.prefix(length))
1400  self.forward(length)
1401  length = 0
1402  chunks.append(self.scan_uri_escapes(name, start_mark))
1403  else:
1404  length += 1
1405  ch = self.peek(length)
1406  if length:
1407  chunks.append(self.prefix(length))
1408  self.forward(length)
1409  length = 0
1410  if not chunks:
1411  raise ScannerError("while parsing a %s" % name, start_mark,
1412  "expected URI, but found %r" % ch.encode('utf-8'),
1413  self.get_mark())
1414  return u''.join(chunks)
1415 
1416  def scan_uri_escapes(self, name, start_mark):
1417  # See the specification for details.
1418  bytes = []
1419  mark = self.get_mark()
1420  while self.peek() == u'%':
1421  self.forward()
1422  for k in range(2):
1423  if self.peek(k) not in u'0123456789ABCDEFabcdef':
1424  raise ScannerError("while scanning a %s" % name, start_mark,
1425  "expected URI escape sequence of 2 hexdecimal numbers, but found %r" %
1426  (self.peek(k).encode('utf-8')), self.get_mark())
1427  bytes.append(chr(int(self.prefix(2), 16)))
1428  self.forward(2)
1429  try:
1430  value = unicode(''.join(bytes), 'utf-8')
1431  except UnicodeDecodeError, exc:
1432  raise ScannerError("while scanning a %s" % name, start_mark, str(exc), mark)
1433  return value
1434 
1435  def scan_line_break(self):
1436  # Transforms:
1437  # '\r\n' : '\n'
1438  # '\r' : '\n'
1439  # '\n' : '\n'
1440  # '\x85' : '\n'
1441  # '\u2028' : '\u2028'
1442  # '\u2029 : '\u2029'
1443  # default : ''
1444  ch = self.peek()
1445  if ch in u'\r\n\x85':
1446  if self.prefix(2) == u'\r\n':
1447  self.forward(2)
1448  else:
1449  self.forward()
1450  return u'\n'
1451  elif ch in u'\u2028\u2029':
1452  self.forward()
1453  return ch
1454  return u''
1455 
1456 #try:
1457 # import psyco
1458 # psyco.bind(Scanner)
1459 #except ImportError:
1460 # pass
1461 
def scan_block_scalar_ignored_line
Definition: scanner.py:1103
def scan_yaml_directive_number
Definition: scanner.py:851
def scan_tag_directive_handle
Definition: scanner.py:875
def next_possible_simple_key
Definition: scanner.py:266
def fetch_document_indicator
Definition: scanner.py:413
def fetch_flow_mapping_end
Definition: scanner.py:455
def scan_block_scalar_breaks
Definition: scanner.py:1132
Fstring::size_type len(Fstring const &s)
Length.
Definition: Fstring.hh:2207
Definition: tokens.py:74
def remove_possible_simple_key
Definition: scanner.py:318
def save_possible_simple_key
Definition: scanner.py:297
def fetch_flow_sequence_start
Definition: scanner.py:429
def scan_tag_directive_prefix
Definition: scanner.py:885
def fetch_flow_sequence_end
Definition: scanner.py:452
def scan_block_scalar_indicators
Definition: scanner.py:1063
def stale_possible_simple_keys
Definition: scanner.py:281
def scan_tag_directive_value
Definition: scanner.py:865
def scan_yaml_directive_value
Definition: scanner.py:832
def scan_block_scalar_indentation
Definition: scanner.py:1117
def scan_directive_ignored_line
Definition: scanner.py:895
def fetch_flow_collection_end
Definition: scanner.py:458
static T max(T x, T y)
Definition: Svm.cc:19
def fetch_flow_mapping_start
Definition: scanner.py:432
indent
In flow context, tokens should respect indentation.
Definition: scanner.py:81
def fetch_flow_collection_start
Definition: scanner.py:435
Definition: tokens.py:77