Ignore:
Timestamp:
04/09/06 15:51:02 (9 years ago)
Author:
xi
Message:

Emitter is done!!!

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pyyaml/trunk/lib/yaml/emitter.py

    r131 r132  
    1515    pass 
    1616 
     17class ScalarAnalysis: 
     18    def __init__(self, scalar, empty, multiline, 
     19            allow_flow_plain, allow_block_plain, 
     20            allow_single_quoted, allow_double_quoted, allow_block): 
     21        self.scalar = scalar 
     22        self.empty = empty 
     23        self.multiline = multiline 
     24        self.allow_flow_plain = allow_flow_plain 
     25        self.allow_block_plain = allow_block_plain 
     26        self.allow_single_quoted = allow_single_quoted 
     27        self.allow_double_quoted = allow_double_quoted 
     28        self.allow_block = allow_block 
     29 
    1730class Emitter: 
    1831 
     
    5467        # Characteristics of the last emitted character: 
    5568        #  - current position. 
    56         #  - is it a line break? 
    5769        #  - is it a whitespace? 
    5870        #  - is it an indention character 
     
    7082        self.tag_prefixes = None 
    7183 
    72         # Scalar analysis. 
    73         self.analysis = None 
     84        # Analyses cache. 
     85        self.anchor_text = None 
     86        self.tag_text = None 
     87        self.scalar_analysis = None 
     88        self.scalar_style = None 
    7489 
    7590    def emit(self, event): 
    76         if self.events: 
    77             self.events.append(event) 
    78             event = self.events.pop(0) 
    79         self.event = event 
    80         if self.need_more_events(): 
    81             self.event.insert(0, event) 
    82             return 
    83         self.state() 
    84         self.event = None 
     91        self.events.append(event) 
     92        while not self.need_more_events(): 
     93            self.event = self.events.pop(0) 
     94            self.state() 
     95            self.event = None 
    8596 
    8697    # In some cases, we wait for a few next events before emitting. 
    8798 
    8899    def need_more_events(self): 
    89         if isinstance(self.event, DocumentStartEvent): 
     100        if not self.events: 
     101            return True 
     102        event = self.events[0] 
     103        if isinstance(event, DocumentStartEvent): 
    90104            return self.need_events(1) 
    91         elif isinstance(self.event, SequenceStartEvent): 
     105        elif isinstance(event, SequenceStartEvent): 
    92106            return self.need_events(2) 
    93         elif isinstance(self.event, MappingStartEvent): 
     107        elif isinstance(event, MappingStartEvent): 
    94108            return self.need_events(3) 
    95109        else: 
     
    98112    def need_events(self, count): 
    99113        level = 0 
    100         for event in self.events: 
    101             if isinstance(event, (DocumentStart, CollectionStart)): 
     114        for event in self.events[1:]: 
     115            if isinstance(event, (DocumentStartEvent, CollectionStartEvent)): 
    102116                level += 1 
    103             elif isinstance(event, (DocumentEnd, CollectionEnd)): 
     117            elif isinstance(event, (DocumentEndEvent, CollectionEndEvent)): 
    104118                level -= 1 
    105             elif isinstance(event, StreamEnd): 
     119            elif isinstance(event, StreamEndEvent): 
    106120                level = -1 
    107121            if level < 0: 
    108122                return False 
    109         return (len(self.events) < count) 
     123        return (len(self.events) < count+1) 
    110124 
    111125    def increase_indent(self, flow=False, indentless=False): 
     
    125139    def expect_stream_start(self): 
    126140        if isinstance(self.event, StreamStartEvent): 
    127             self.encoding = event.encoding 
    128             self.canonical = event.canonical 
     141            self.encoding = self.event.encoding 
     142            self.canonical = self.event.canonical 
    129143            if self.event.indent and self.event.indent > 1: 
    130144                self.best_indent = self.event.indent 
     
    150164        if isinstance(self.event, DocumentStartEvent): 
    151165            if self.event.version: 
    152                 self.write_version_directive(self.event.version) 
     166                version_text = self.analyze_version(self.event.version) 
     167                self.write_version_directive(version_text) 
    153168            self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy() 
    154169            if self.event.tags: 
    155                 for handle in self.event.tags: 
     170                handles = self.event.tags.keys() 
     171                handles.sort() 
     172                for handle in handles: 
    156173                    prefix = self.event.tags[handle] 
    157174                    self.tag_prefixes[prefix] = handle 
    158                     self.write_tag_directive(handle, prefix) 
    159             implicit = (first and self.event.implicit and not self.canonical 
     175                    handle_text = self.analyze_tag_handle(handle) 
     176                    prefix_text = self.analyze_tag_prefix(prefix) 
     177                    self.write_tag_directive(handle_text, prefix_text) 
     178            implicit = (first and not self.event.explicit and not self.canonical 
    160179                    and not self.event.version and not self.event.tags 
    161                     and not self.check_next_empty_scalar()) 
     180                    and not self.check_empty_document()) 
    162181            if not implicit: 
    163182                self.write_indent() 
     
    176195        if isinstance(self.event, DocumentEndEvent): 
    177196            self.write_indent() 
    178             if not event.implicit: 
     197            if self.event.explicit: 
    179198                self.write_indicator(u'...', True) 
    180199                self.write_indent() 
     
    185204 
    186205    def expect_document_root(self): 
     206        self.states.append(self.expect_document_end) 
    187207        self.expect_node(root=True) 
    188208 
     
    197217        if isinstance(self.event, AliasEvent): 
    198218            self.expect_alias() 
    199         elif isinstance(event, (ScalarEvent, CollectionEvent)): 
    200             self.process_anchor() 
     219        elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)): 
     220            self.process_anchor(u'&') 
    201221            self.process_tag() 
    202222            if isinstance(self.event, ScalarEvent): 
    203223                self.expect_scalar() 
    204             elif isinstance(self.event, SequenceEvent): 
     224            elif isinstance(self.event, SequenceStartEvent): 
    205225                if self.flow_level or self.canonical or self.event.flow_style   \ 
    206226                        or self.check_empty_sequence(): 
     
    208228                else: 
    209229                    self.expect_block_sequence() 
    210             elif isinstance(self.event, MappingEvent): 
     230            elif isinstance(self.event, MappingStartEvent): 
    211231                if self.flow_level or self.canonical or self.event.flow_style   \ 
    212232                        or self.check_empty_mapping(): 
     
    218238 
    219239    def expect_alias(self): 
    220         self.write_anchor(u'*', self.event.anchor) 
     240        if self.event.anchor is None: 
     241            raise EmitterError("anchor is not specified for alias") 
     242        self.process_anchor(u'*') 
    221243        self.state = self.states.pop() 
    222244 
     
    351373 
    352374    def expect_block_mapping_key(self, first=False): 
    353         if not first and isinstance(self.event, SequenceEndEvent): 
     375        if not first and isinstance(self.event, MappingEndEvent): 
    354376            self.indent = self.indents.pop() 
    355377            self.state = self.states.pop() 
     
    375397        self.expect_node(mapping=True) 
    376398 
     399    # Checkers. 
     400 
     401    def check_empty_sequence(self): 
     402        return (isinstance(self.event, SequenceStartEvent) and self.events 
     403                and isinstance(self.events[0], SequenceEndEvent)) 
     404 
     405    def check_empty_mapping(self): 
     406        return (isinstance(self.event, MappingStartEvent) and self.events 
     407                and isinstance(self.events[0], MappingEndEvent)) 
     408 
     409    def check_empty_document(self): 
     410        if not isinstance(self.event, DocumentStartEvent) or not self.events: 
     411            return False 
     412        event = self.events[0] 
     413        return (isinstance(event, ScalarEvent) and event.anchor is None 
     414                and event.tag is None and event.implicit and event.value == u'') 
     415 
     416    def check_simple_key(self): 
     417        length = 0 
     418        if isinstance(self.event, NodeEvent) and self.event.anchor is not None: 
     419            if self.anchor_text is None: 
     420                self.anchor_text = self.analyze_anchor(self.event.anchor) 
     421            length += len(self.anchor_text) 
     422        if isinstance(self.event, (ScalarEvent, CollectionStartEvent))  \ 
     423                and self.event.tag is not None: 
     424            if self.tag_text is None: 
     425                self.tag_text = self.analyze_tag(self.event.tag) 
     426            length += len(self.tag_text) 
     427        if isinstance(self.event, ScalarEvent): 
     428            if self.scalar_analysis is None: 
     429                self.scalar_analysis = self.analyze_scalar(self.event.value) 
     430            length += len(self.scalar_analysis.scalar) 
     431        return (length < 128 and (isinstance(self.event, AliasEvent) 
     432            or (isinstance(self.event, ScalarEvent) and not self.scalar_analysis.multiline) 
     433            or self.check_empty_sequence() or self.check_empty_mapping())) 
     434 
     435    # Anchor, Tag, and Scalar processors. 
     436 
     437    def process_anchor(self, indicator): 
     438        if self.event.anchor is None: 
     439            return 
     440        if self.anchor_text is None: 
     441            self.anchor_text = self.analyze_anchor(self.event.anchor) 
     442        if self.anchor_text: 
     443            self.write_indicator(indicator+self.anchor_text, True) 
     444        self.anchor_text = None 
     445 
     446    def process_tag(self): 
     447        if self.event.tag is None: 
     448            return 
     449        if isinstance(self.event, ScalarEvent) and self.best_scalar_style() == '': 
     450            return 
     451        if self.tag_text is None: 
     452            self.tag_text = self.analyze_tag(self.event.tag) 
     453        if self.tag_text: 
     454            self.write_indicator(self.tag_text, True) 
     455        self.tag_text = None 
     456 
     457    def best_scalar_style(self): 
     458        if self.scalar_analysis is None: 
     459            self.scalar_analysis = self.analyze_scalar(self.event.value) 
     460        if self.canonical: 
     461            return '"' 
     462        if (self.event.implicit and not self.event.style 
     463                and ((self.flow_level and self.scalar_analysis.allow_flow_plain) 
     464                    or (not self.flow_level and self.scalar_analysis.allow_block_plain)) 
     465                and (len(self.scalar_analysis.scalar) > 0 
     466                    or (not self.flow_level and not self.simple_key_context))): 
     467            return '' 
     468        elif self.event.style == '\'' and self.scalar_analysis.allow_single_quoted: 
     469            return '\'' 
     470        elif self.event.style in ['|', '>'] and not self.flow_level and self.scalar_analysis.allow_block: 
     471            return self.event.style 
     472        else: 
     473            return '"' 
     474        return style 
     475 
     476    def process_scalar(self): 
     477        if self.scalar_analysis is None: 
     478            self.scalar_analysis = self.analyze_scalar(self.event.value) 
     479        style = self.best_scalar_style() 
     480        if self.scalar_analysis.multiline and not self.simple_key_context   \ 
     481                and style not in ['|', '>']: 
     482            self.write_indent() 
     483        if style == '"': 
     484            self.write_double_quoted(self.scalar_analysis.scalar, 
     485                    split=(not self.simple_key_context)) 
     486        elif style == '\'': 
     487            self.write_single_quoted(self.scalar_analysis.scalar, 
     488                    split=(not self.simple_key_context)) 
     489        elif style == '>': 
     490            self.write_folded(self.scalar_analysis.scalar) 
     491        elif style == '|': 
     492            self.write_literal(self.scalar_analysis.scalar) 
     493        else: 
     494            self.write_plain(self.scalar_analysis.scalar, 
     495                    split=(not self.simple_key_context)) 
     496        self.scalar_analysis = None 
     497 
     498    # Analyzers. 
     499 
     500    def analyze_version(self, version): 
     501        major, minor = version 
     502        if major != 1: 
     503            raise EmitterError("unsupported YAML version: %d.%d" % (major, minor)) 
     504        return u'%d.%d' % (major, minor) 
     505 
     506    def analyze_tag_handle(self, handle): 
     507        if not handle: 
     508            raise EmitterError("tag handle must not be empty") 
     509        if handle[0] != u'!' or handle[-1] != u'!': 
     510            raise EmitterError("tag handle must start and end with '!': %r" 
     511                    % (handle.encode('utf-8'))) 
     512        for ch in handle[1:-1]: 
     513            if not (u'0' <= ch <= u'9' or u'A' <= ch <= 'Z' or u'a' <= ch <= 'z'    \ 
     514                    or ch in u'-_'): 
     515                raise EmitterError("invalid character %r in the tag handle: %r" 
     516                        % (ch.encode('utf-8'), handle.encode('utf-8'))) 
     517        return handle 
     518 
     519    def analyze_tag_prefix(self, prefix): 
     520        if not prefix: 
     521            raise EmitterError("tag prefix must not be empty") 
     522        chunks = [] 
     523        start = end = 0 
     524        if prefix[0] == u'!': 
     525            end = 1 
     526        while end < len(prefix): 
     527            ch = prefix[end] 
     528            if u'0' <= ch <= u'9' or u'A' <= ch <= 'Z' or u'a' <= ch <= 'z'  \ 
     529                    or ch in u'-;/?!:@&=+$,_.~*\'()[]': 
     530                end += 1 
     531            else: 
     532                if start < end: 
     533                    chunks.append(prefix[start:end]) 
     534                start = end = end+1 
     535                data = ch.encode('utf-8') 
     536                for ch in data: 
     537                    chunks.append(u'%%%02X' % ord(ch)) 
     538        if start < end: 
     539            chunks.append(prefix[start:end]) 
     540        return u''.join(chunks) 
     541 
     542    def analyze_tag(self, tag): 
     543        if not tag: 
     544            raise EmitterError("tag must not be empty") 
     545        handle = None 
     546        suffix = tag 
     547        for prefix in self.tag_prefixes: 
     548            if tag.startswith(prefix)   \ 
     549                    and (prefix == u'!' or len(prefix) < len(tag)): 
     550                handle = self.tag_prefixes[prefix] 
     551                suffix = tag[len(prefix):] 
     552        chunks = [] 
     553        start = end = 0 
     554        while end < len(suffix): 
     555            ch = suffix[end] 
     556            if u'0' <= ch <= u'9' or u'A' <= ch <= 'Z' or u'a' <= ch <= 'z'  \ 
     557                    or ch in u'-;/?:@&=+$,_.~*\'()[]'   \ 
     558                    or (ch == u'!' and handle != u'!'): 
     559                end += 1 
     560            else: 
     561                if start < end: 
     562                    chunks.append(suffix[start:end]) 
     563                start = end = end+1 
     564                data = ch.encode('utf-8') 
     565                for ch in data: 
     566                    chunks.append(u'%%%02X' % ord(ch)) 
     567        if start < end: 
     568            chunks.append(suffix[start:end]) 
     569        suffix_text = u''.join(chunks) 
     570        if handle: 
     571            return u'%s%s' % (handle, suffix_text) 
     572        else: 
     573            return u'!<%s>' % suffix_text 
     574 
     575    def analyze_anchor(self, anchor): 
     576        if not anchor: 
     577            raise EmitterError("anchor must not be empty") 
     578        for ch in anchor: 
     579            if not (u'0' <= ch <= u'9' or u'A' <= ch <= 'Z' or u'a' <= ch <= 'z'    \ 
     580                    or ch in u'-_'): 
     581                raise EmitterError("invalid character %r in the anchor: %r" 
     582                        % (ch.encode('utf-8'), text.encode('utf-8'))) 
     583        return anchor 
     584 
     585    def analyze_scalar(self, scalar):   # It begs for refactoring. 
     586        if not scalar: 
     587            return ScalarAnalysis(scalar=scalar, empty=True, multiline=False, 
     588                    allow_flow_plain=False, allow_block_plain=True, 
     589                    allow_single_quoted=True, allow_double_quoted=True, 
     590                    allow_block=False) 
     591        contains_block_indicator = False 
     592        contains_flow_indicator = False 
     593        contains_line_breaks = False 
     594        contains_unicode_characters = False 
     595        contains_special_characters = False 
     596        contains_inline_spaces = False          # non-space space+ non-space 
     597        contains_inline_breaks = False          # non-space break+ non-space 
     598        contains_leading_spaces = False         # ^ space+ (non-space | $) 
     599        contains_leading_breaks = False         # ^ break+ (non-space | $) 
     600        contains_trailing_spaces = False        # non-space space+ $ 
     601        contains_trailing_breaks = False        # non-space break+ $ 
     602        contains_inline_breaks_spaces = False   # non-space break+ space+ non-space 
     603        contains_mixed_breaks_spaces = False    # anything else 
     604        if scalar.startswith(u'---') or scalar.startswith(u'...'): 
     605            contains_block_indicator = True 
     606            contains_flow_indicator = True 
     607        first = True 
     608        last = (len(scalar) == 1) 
     609        preceeded_by_space = False 
     610        followed_by_space = (len(scalar) > 1 and 
     611                scalar[1] in u'\0 \t\r\n\x85\u2028\u2029') 
     612        spaces = breaks = mixed = leading = False 
     613        index = 0 
     614        while index < len(scalar): 
     615            ch = scalar[index] 
     616            if first: 
     617                if ch in u'#,[]{}#&*!|>\'\"%@`':  
     618                    contains_flow_indicator = True 
     619                    contains_block_indicator = True 
     620                if ch in u'?:': 
     621                    contains_flow_indicator = True 
     622                    if followed_by_space or last: 
     623                        contains_block_indicator = True 
     624                if ch == u'-' and followed_by_space or last: 
     625                    contains_flow_indicator = True 
     626                    contains_block_indicator = True 
     627            else: 
     628                if ch in u',?[]{}': 
     629                    contains_flow_indicator = True 
     630                if ch == u':': 
     631                    contains_flow_indicator = True 
     632                    if followed_by_space or last: 
     633                        contains_block_indicator = True 
     634                if ch == u'#' and preceeded_by_space: 
     635                    contains_flow_indicator = True 
     636                    contains_block_indicator = True 
     637            if ch in u'\n\x85\u2028\u2029': 
     638                contains_line_breaks = True 
     639            if not (ch == u'\n' or u'\x20' <= ch <= u'\x7E'): 
     640                if ch < u'\x80': 
     641                    contains_special_characters = True 
     642                else: 
     643                    contains_special_characters = True 
     644                    # TODO: We need an option to allow unescaped unicode 
     645                    # characters. 
     646                    contains_unicode_characters = True 
     647            if ch == u' ': 
     648                if not spaces and not breaks: 
     649                    leading = first 
     650                spaces = True 
     651            elif ch in u'\n\x85\u2028\u2029': 
     652                if not spaces and not breaks: 
     653                    leading = first 
     654                breaks = True 
     655                if spaces: 
     656                    mixed = True 
     657            if ch not in u' \n\x85\u2028\u2029': 
     658                if leading: 
     659                    if spaces and breaks: 
     660                        contains_mixed_breaks_spaces = True 
     661                    elif spaces: 
     662                        contains_leading_spaces = True 
     663                    elif breaks: 
     664                        contains_leading_breaks = True 
     665                else: 
     666                    if mixed: 
     667                        contains_mixed_break_spaces = True 
     668                    elif spaces and breaks: 
     669                        contains_inline_breaks_spaces = True 
     670                    elif spaces: 
     671                        contains_inline_spaces = True 
     672                    elif breaks: 
     673                        contains_inline_breaks = True 
     674                spaces = breaks = mixed = leading = False 
     675            elif last: 
     676                if spaces and breaks: 
     677                    contains_mixed_break_spaces = True 
     678                elif spaces: 
     679                    if leading: 
     680                        contains_leading_spaces = True 
     681                    else: 
     682                        contains_trailing_spaces = True 
     683                elif breaks: 
     684                    if leading: 
     685                        contains_leading_breaks = True 
     686                    else: 
     687                        contains_trailing_breaks = True 
     688            index += 1 
     689            first = False 
     690            last = (index+1 == len(scalar)) 
     691            preceeded_by_space = (ch in u'\0 \t\r\n\x85\u2028\u2029') 
     692            followed_by_space = (index+1 < len(scalar) and 
     693                    scalar[index+1] in u'\0 \t\r\n\x85\u2028\u2029') 
     694        allow_flow_plain = not (contains_flow_indicator or contains_special_characters 
     695            or contains_leading_spaces or contains_leading_breaks 
     696            or contains_trailing_spaces or contains_trailing_breaks 
     697            or contains_inline_breaks_spaces or contains_mixed_breaks_spaces) 
     698        allow_block_plain = not (contains_block_indicator or contains_special_characters 
     699            or contains_leading_spaces or contains_leading_breaks 
     700            or contains_trailing_spaces or contains_trailing_breaks 
     701            or contains_inline_breaks_spaces or contains_mixed_breaks_spaces) 
     702        allow_single_quoted = not (contains_special_characters 
     703            or contains_inline_breaks_spaces or contains_mixed_breaks_spaces) 
     704        allow_double_quoted = True 
     705        allow_block = not (contains_special_characters 
     706            or contains_leading_spaces or contains_leading_breaks 
     707            or contains_trailing_spaces or contains_mixed_breaks_spaces) 
     708        return ScalarAnalysis(scalar=scalar, empty=False, multiline=contains_line_breaks, 
     709                allow_flow_plain=allow_flow_plain, allow_block_plain=allow_block_plain, 
     710                allow_single_quoted=allow_single_quoted, allow_double_quoted=allow_double_quoted, 
     711                allow_block=allow_block) 
     712 
    377713    # Writers. 
    378714 
     
    388724    def write_indicator(self, indicator, need_whitespace, 
    389725            whitespace=False, indention=False): 
    390         if self.whitespace: 
     726        if self.whitespace or not need_whitespace: 
    391727            data = indicator 
    392728        else: 
    393729            data = u' '+indicator 
    394         self.writespace = whitespace 
     730        self.whitespace = whitespace 
    395731        self.indention = self.indention and indention 
    396732        self.column += len(data) 
     
    401737    def write_indent(self): 
    402738        indent = self.indent or 0 
    403         if not self.indention or self.column > indent: 
     739        if not self.indention or self.column > indent   \ 
     740                or (self.column == indent and not self.whitespace): 
    404741            self.write_line_break() 
    405742        if self.column < indent: 
     743            self.whitespace = True 
    406744            data = u' '*(indent-self.column) 
    407745            self.column = indent 
     
    410748            self.writer.write(data) 
    411749 
    412     def write_line_break(self): 
    413         data = self.best_line_break 
     750    def write_line_break(self, data=None): 
     751        if data is None: 
     752            data = self.best_line_break 
    414753        self.whitespace = True 
    415754        self.indention = True 
     
    420759        self.writer.write(data) 
    421760 
     761    def write_version_directive(self, version_text): 
     762        data = u'%%YAML %s' % version_text 
     763        if self.encoding: 
     764            data = data.encode(self.encoding) 
     765        self.writer.write(data) 
     766        self.write_line_break() 
     767 
     768    def write_tag_directive(self, handle_text, prefix_text): 
     769        data = u'%%TAG %s %s' % (handle_text, prefix_text) 
     770        if self.encoding: 
     771            data = data.encode(self.encoding) 
     772        self.writer.write(data) 
     773        self.write_line_break() 
     774 
     775    # Scalar writers. 
     776 
     777    def write_single_quoted(self, text, split=True): 
     778        self.write_indicator(u'\'', True) 
     779        spaces = False 
     780        breaks = False 
     781        start = end = 0 
     782        while end <= len(text): 
     783            ch = None 
     784            if end < len(text): 
     785                ch = text[end] 
     786            if spaces: 
     787                if ch is None or ch != u' ': 
     788                    if start+1 == end and self.column > self.best_width and split   \ 
     789                            and start != 0 and end != len(text): 
     790                        self.write_indent() 
     791                    else: 
     792                        data = text[start:end] 
     793                        self.column += len(data) 
     794                        if self.encoding: 
     795                            data = data.encode(self.encoding) 
     796                        self.writer.write(data) 
     797                    start = end 
     798            elif breaks: 
     799                if ch is None or ch not in u'\n\x85\u2028\u2029': 
     800                    if text[start] == u'\n': 
     801                        self.write_line_break() 
     802                    for br in text[start:end]: 
     803                        if br == u'\n': 
     804                            self.write_line_break() 
     805                        else: 
     806                            self.write_line_break(br) 
     807                    self.write_indent() 
     808                    start = end 
     809            else: 
     810                if ch is None or ch in u' \n\x85\u2028\u2029' or ch == u'\'': 
     811                    if start < end: 
     812                        data = text[start:end] 
     813                        self.column += len(data) 
     814                        if self.encoding: 
     815                            data = data.encode(self.encoding) 
     816                        self.writer.write(data) 
     817                        start = end 
     818                    if ch == u'\'': 
     819                        data = u'\'\'' 
     820                        self.column += 2 
     821                        if self.encoding: 
     822                            data = data.encode(self.encoding) 
     823                        self.writer.write(data) 
     824                        start = end + 1 
     825            if ch is not None: 
     826                spaces = (ch == u' ') 
     827                breaks = (ch in u'\n\x85\u2028\u2029') 
     828            end += 1 
     829        self.write_indicator(u'\'', False) 
     830 
     831    ESCAPE_REPLACEMENTS = { 
     832        u'\0':      u'0', 
     833        u'\x07':    u'a', 
     834        u'\x08':    u'b', 
     835        u'\x09':    u't', 
     836        u'\x0A':    u'n', 
     837        u'\x0B':    u'v', 
     838        u'\x0C':    u'f', 
     839        u'\x0D':    u'r', 
     840        u'\x1B':    u'e', 
     841        u'\"':      u'\"', 
     842        u'\\':      u'\\', 
     843        u'\x85':    u'N', 
     844        u'\xA0':    u'_', 
     845        u'\u2028':  u'L', 
     846        u'\u2029':  u'P', 
     847    } 
     848 
     849    def write_double_quoted(self, text, split=True): 
     850        self.write_indicator(u'"', True) 
     851        start = end = 0 
     852        while end <= len(text): 
     853            ch = None 
     854            if end < len(text): 
     855                ch = text[end] 
     856            if ch is None or not (u'\x20' <= ch <= u'\x7E') or ch in u'"\\': 
     857                if start < end: 
     858                    data = text[start:end] 
     859                    self.column += len(data) 
     860                    if self.encoding: 
     861                        data = data.encode(self.encoding) 
     862                    self.writer.write(data) 
     863                    start = end 
     864                if ch is not None: 
     865                    if ch in self.ESCAPE_REPLACEMENTS: 
     866                        data = u'\\'+self.ESCAPE_REPLACEMENTS[ch] 
     867                    elif ch <= u'\xFF': 
     868                        data = u'\\x%02X' % ord(ch) 
     869                    elif ch <= u'\uFFFF': 
     870                        data = u'\\u%04X' % ord(ch) 
     871                    else: 
     872                        data = u'\\U%08X' % ord(ch) 
     873                    self.column += len(data) 
     874                    if self.encoding: 
     875                        data = data.encode(self.encoding) 
     876                    self.writer.write(data) 
     877                    start = end+1 
     878            if 0 < end < len(text)-1 and (ch == u' ' or start >= end)   \ 
     879                    and self.column+(end-start) > self.best_width and split: 
     880                data = text[start:end]+u'\\' 
     881                if start < end: 
     882                    start = end 
     883                self.column += len(data) 
     884                if self.encoding: 
     885                    data = data.encode(self.encoding) 
     886                self.writer.write(data) 
     887                self.write_indent() 
     888                self.whitespace = False 
     889                self.indention = False 
     890                if ch == u' ': 
     891                    data = u'\\' 
     892                    self.column += len(data) 
     893                    if self.encoding: 
     894                        data = data.encode(self.encoding) 
     895                    self.writer.write(data) 
     896            end += 1 
     897        self.write_indicator(u'"', False) 
     898 
     899    def determine_chomp(self, text): 
     900        tail = text[-2:] 
     901        while len(tail) < 2: 
     902            tail = u' '+tail 
     903        if tail[-1] in u'\n\x85\u2028\u2029': 
     904            if tail[-2] in u'\n\x85\u2028\u2029': 
     905                return u'+' 
     906            else: 
     907                return u'' 
     908        else: 
     909            return u'-' 
     910 
     911    def write_folded(self, text): 
     912        chomp = self.determine_chomp(text) 
     913        self.write_indicator(u'>'+chomp, True) 
     914        self.write_indent() 
     915        leading_space = False 
     916        spaces = False 
     917        breaks = False 
     918        start = end = 0 
     919        while end <= len(text): 
     920            ch = None 
     921            if end < len(text): 
     922                ch = text[end] 
     923            if breaks: 
     924                if ch is None or ch not in u'\n\x85\u2028\u2029': 
     925                    if not leading_space and ch is not None and ch != u' '  \ 
     926                            and text[start] == u'\n': 
     927                        self.write_line_break() 
     928                    leading_space = (ch == u' ') 
     929                    for br in text[start:end]: 
     930                        if br == u'\n': 
     931                            self.write_line_break() 
     932                        else: 
     933                            self.write_line_break(br) 
     934                    if ch is not None: 
     935                        self.write_indent() 
     936                    start = end 
     937            elif spaces: 
     938                if ch != u' ': 
     939                    if start+1 == end and self.column > self.best_width: 
     940                        self.write_indent() 
     941                    else: 
     942                        data = text[start:end] 
     943                        self.column += len(data) 
     944                        if self.encoding: 
     945                            data = data.encode(self.encoding) 
     946                        self.writer.write(data) 
     947                    start = end 
     948            else: 
     949                if ch is None or ch in u' \n\x85\u2028\u2029': 
     950                    data = text[start:end] 
     951                    if self.encoding: 
     952                        data = data.encode(self.encoding) 
     953                    self.writer.write(data) 
     954                    if ch is None: 
     955                        self.write_line_break() 
     956                    start = end 
     957            if ch is not None: 
     958                breaks = (ch in u'\n\x85\u2028\u2029') 
     959                spaces = (ch == u' ') 
     960            end += 1 
     961 
     962    def write_literal(self, text): 
     963        chomp = self.determine_chomp(text) 
     964        self.write_indicator(u'|'+chomp, True) 
     965        self.write_indent() 
     966        breaks = False 
     967        start = end = 0 
     968        while end <= len(text): 
     969            ch = None 
     970            if end < len(text): 
     971                ch = text[end] 
     972            if breaks: 
     973                if ch is None or ch not in u'\n\x85\u2028\u2029': 
     974                    for br in text[start:end]: 
     975                        if br == u'\n': 
     976                            self.write_line_break() 
     977                        else: 
     978                            self.write_line_break(br) 
     979                    if ch is not None: 
     980                        self.write_indent() 
     981                    start = end 
     982            else: 
     983                if ch is None or ch in u'\n\x85\u2028\u2029': 
     984                    data = text[start:end] 
     985                    if self.encoding: 
     986                        data = data.encode(self.encoding) 
     987                    self.writer.write(data) 
     988                    if ch is None: 
     989                        self.write_line_break() 
     990                    start = end 
     991            if ch is not None: 
     992                breaks = (ch in u'\n\x85\u2028\u2029') 
     993            end += 1 
     994 
     995    def write_plain(self, text, split=True): 
     996        if not text: 
     997            return 
     998        if not self.whitespace: 
     999            data = u' ' 
     1000            self.column += len(data) 
     1001            if self.encoding: 
     1002                data = data.encode(self.encoding) 
     1003            self.writer.write(data) 
     1004        self.writespace = False 
     1005        self.indention = False 
     1006        spaces = False 
     1007        breaks = False 
     1008        start = end = 0 
     1009        while end <= len(text): 
     1010            ch = None 
     1011            if end < len(text): 
     1012                ch = text[end] 
     1013            if spaces: 
     1014                if ch != u' ': 
     1015                    if start+1 == end and self.column > self.best_width and split: 
     1016                        self.write_indent() 
     1017                        self.writespace = False 
     1018                        self.indention = False 
     1019                    else: 
     1020                        data = text[start:end] 
     1021                        self.column += len(data) 
     1022                        if self.encoding: 
     1023                            data = data.encode(self.encoding) 
     1024                        self.writer.write(data) 
     1025                    start = end 
     1026            elif breaks: 
     1027                if ch not in u'\n\x85\u2028\u2029': 
     1028                    if text[start] == u'\n': 
     1029                        self.write_line_break() 
     1030                    for br in text[start:end]: 
     1031                        if br == u'\n': 
     1032                            self.write_line_break() 
     1033                        else: 
     1034                            self.write_line_break(br) 
     1035                    self.write_indent() 
     1036                    self.whitespace = False 
     1037                    self.indention = False 
     1038                    start = end 
     1039            else: 
     1040                if ch is None or ch in u' \n\x85\u2028\u2029': 
     1041                    data = text[start:end] 
     1042                    self.column += len(data) 
     1043                    if self.encoding: 
     1044                        data = data.encode(self.encoding) 
     1045                    self.writer.write(data) 
     1046                    start = end 
     1047            if ch is not None: 
     1048                spaces = (ch == u' ') 
     1049                breaks = (ch in u'\n\x85\u2028\u2029') 
     1050            end += 1 
     1051 
Note: See TracChangeset for help on using the changeset viewer.