buildframework/helium/external/python/lib/common/docutils-0.5-py2.5.egg/docutils/statemachine.py
author wbernard
Wed, 23 Dec 2009 19:29:07 +0200
changeset 179 d8ac696cc51f
permissions -rw-r--r--
helium_7.0-r14027
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
179
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
     1
# $Id: statemachine.py 4564 2006-05-21 20:44:42Z wiemann $
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
     2
# Author: David Goodger <goodger@python.org>
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
     3
# Copyright: This module has been placed in the public domain.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
     4
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
     5
"""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
     6
A finite state machine specialized for regular-expression-based text filters,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
     7
this module defines the following classes:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
     8
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
     9
- `StateMachine`, a state machine
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    10
- `State`, a state superclass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    11
- `StateMachineWS`, a whitespace-sensitive version of `StateMachine`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    12
- `StateWS`, a state superclass for use with `StateMachineWS`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    13
- `SearchStateMachine`, uses `re.search()` instead of `re.match()`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    14
- `SearchStateMachineWS`, uses `re.search()` instead of `re.match()`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    15
- `ViewList`, extends standard Python lists.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    16
- `StringList`, string-specific ViewList.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    17
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    18
Exception classes:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    19
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    20
- `StateMachineError`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    21
- `UnknownStateError`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    22
- `DuplicateStateError`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    23
- `UnknownTransitionError`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    24
- `DuplicateTransitionError`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    25
- `TransitionPatternNotFound`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    26
- `TransitionMethodNotFound`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    27
- `UnexpectedIndentationError`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    28
- `TransitionCorrection`: Raised to switch to another transition.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    29
- `StateCorrection`: Raised to switch to another state & transition.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    30
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    31
Functions:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    32
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    33
- `string2lines()`: split a multi-line string into a list of one-line strings
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    34
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    35
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    36
How To Use This Module
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    37
======================
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    38
(See the individual classes, methods, and attributes for details.)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    39
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    40
1. Import it: ``import statemachine`` or ``from statemachine import ...``.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    41
   You will also need to ``import re``.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    42
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    43
2. Derive a subclass of `State` (or `StateWS`) for each state in your state
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    44
   machine::
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    45
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    46
       class MyState(statemachine.State):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    47
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    48
   Within the state's class definition:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    49
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    50
   a) Include a pattern for each transition, in `State.patterns`::
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    51
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    52
          patterns = {'atransition': r'pattern', ...}
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    53
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    54
   b) Include a list of initial transitions to be set up automatically, in
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    55
      `State.initial_transitions`::
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    56
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    57
          initial_transitions = ['atransition', ...]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    58
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    59
   c) Define a method for each transition, with the same name as the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    60
      transition pattern::
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    61
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    62
          def atransition(self, match, context, next_state):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    63
              # do something
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    64
              result = [...]  # a list
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    65
              return context, next_state, result
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    66
              # context, next_state may be altered
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    67
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    68
      Transition methods may raise an `EOFError` to cut processing short.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    69
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    70
   d) You may wish to override the `State.bof()` and/or `State.eof()` implicit
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    71
      transition methods, which handle the beginning- and end-of-file.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    72
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    73
   e) In order to handle nested processing, you may wish to override the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    74
      attributes `State.nested_sm` and/or `State.nested_sm_kwargs`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    75
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    76
      If you are using `StateWS` as a base class, in order to handle nested
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    77
      indented blocks, you may wish to:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    78
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    79
      - override the attributes `StateWS.indent_sm`,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    80
        `StateWS.indent_sm_kwargs`, `StateWS.known_indent_sm`, and/or
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    81
        `StateWS.known_indent_sm_kwargs`;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    82
      - override the `StateWS.blank()` method; and/or
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    83
      - override or extend the `StateWS.indent()`, `StateWS.known_indent()`,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    84
        and/or `StateWS.firstknown_indent()` methods.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    85
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    86
3. Create a state machine object::
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    87
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    88
       sm = StateMachine(state_classes=[MyState, ...],
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    89
                         initial_state='MyState')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    90
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    91
4. Obtain the input text, which needs to be converted into a tab-free list of
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    92
   one-line strings. For example, to read text from a file called
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    93
   'inputfile'::
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    94
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    95
       input_string = open('inputfile').read()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    96
       input_lines = statemachine.string2lines(input_string)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    97
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    98
5. Run the state machine on the input text and collect the results, a list::
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
    99
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   100
       results = sm.run(input_lines)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   101
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   102
6. Remove any lingering circular references::
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   103
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   104
       sm.unlink()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   105
"""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   106
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   107
__docformat__ = 'restructuredtext'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   108
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   109
import sys
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   110
import re
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   111
import types
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   112
import unicodedata
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   113
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   114
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   115
class StateMachine:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   116
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   117
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   118
    A finite state machine for text filters using regular expressions.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   119
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   120
    The input is provided in the form of a list of one-line strings (no
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   121
    newlines). States are subclasses of the `State` class. Transitions consist
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   122
    of regular expression patterns and transition methods, and are defined in
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   123
    each state.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   124
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   125
    The state machine is started with the `run()` method, which returns the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   126
    results of processing in a list.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   127
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   128
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   129
    def __init__(self, state_classes, initial_state, debug=0):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   130
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   131
        Initialize a `StateMachine` object; add state objects.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   132
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   133
        Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   134
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   135
        - `state_classes`: a list of `State` (sub)classes.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   136
        - `initial_state`: a string, the class name of the initial state.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   137
        - `debug`: a boolean; produce verbose output if true (nonzero).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   138
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   139
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   140
        self.input_lines = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   141
        """`StringList` of input lines (without newlines).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   142
        Filled by `self.run()`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   143
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   144
        self.input_offset = 0
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   145
        """Offset of `self.input_lines` from the beginning of the file."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   146
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   147
        self.line = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   148
        """Current input line."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   149
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   150
        self.line_offset = -1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   151
        """Current input line offset from beginning of `self.input_lines`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   152
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   153
        self.debug = debug
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   154
        """Debugging mode on/off."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   155
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   156
        self.initial_state = initial_state
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   157
        """The name of the initial state (key to `self.states`)."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   158
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   159
        self.current_state = initial_state
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   160
        """The name of the current state (key to `self.states`)."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   161
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   162
        self.states = {}
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   163
        """Mapping of {state_name: State_object}."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   164
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   165
        self.add_states(state_classes)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   166
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   167
        self.observers = []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   168
        """List of bound methods or functions to call whenever the current
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   169
        line changes.  Observers are called with one argument, ``self``.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   170
        Cleared at the end of `run()`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   171
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   172
    def unlink(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   173
        """Remove circular references to objects no longer required."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   174
        for state in self.states.values():
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   175
            state.unlink()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   176
        self.states = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   177
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   178
    def run(self, input_lines, input_offset=0, context=None,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   179
            input_source=None):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   180
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   181
        Run the state machine on `input_lines`. Return results (a list).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   182
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   183
        Reset `self.line_offset` and `self.current_state`. Run the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   184
        beginning-of-file transition. Input one line at a time and check for a
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   185
        matching transition. If a match is found, call the transition method
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   186
        and possibly change the state. Store the context returned by the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   187
        transition method to be passed on to the next transition matched.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   188
        Accumulate the results returned by the transition methods in a list.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   189
        Run the end-of-file transition. Finally, return the accumulated
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   190
        results.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   191
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   192
        Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   193
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   194
        - `input_lines`: a list of strings without newlines, or `StringList`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   195
        - `input_offset`: the line offset of `input_lines` from the beginning
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   196
          of the file.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   197
        - `context`: application-specific storage.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   198
        - `input_source`: name or path of source of `input_lines`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   199
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   200
        self.runtime_init()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   201
        if isinstance(input_lines, StringList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   202
            self.input_lines = input_lines
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   203
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   204
            self.input_lines = StringList(input_lines, source=input_source)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   205
        self.input_offset = input_offset
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   206
        self.line_offset = -1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   207
        self.current_state = self.initial_state
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   208
        if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   209
            print >>sys.stderr, (
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   210
                '\nStateMachine.run: input_lines (line_offset=%s):\n| %s'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   211
                % (self.line_offset, '\n| '.join(self.input_lines)))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   212
        transitions = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   213
        results = []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   214
        state = self.get_state()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   215
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   216
            if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   217
                print >>sys.stderr, ('\nStateMachine.run: bof transition')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   218
            context, result = state.bof(context)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   219
            results.extend(result)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   220
            while 1:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   221
                try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   222
                    try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   223
                        self.next_line()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   224
                        if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   225
                            source, offset = self.input_lines.info(
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   226
                                self.line_offset)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   227
                            print >>sys.stderr, (
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   228
                                '\nStateMachine.run: line (source=%r, '
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   229
                                'offset=%r):\n| %s'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   230
                                % (source, offset, self.line))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   231
                        context, next_state, result = self.check_line(
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   232
                            context, state, transitions)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   233
                    except EOFError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   234
                        if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   235
                            print >>sys.stderr, (
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   236
                                '\nStateMachine.run: %s.eof transition'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   237
                                % state.__class__.__name__)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   238
                        result = state.eof(context)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   239
                        results.extend(result)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   240
                        break
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   241
                    else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   242
                        results.extend(result)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   243
                except TransitionCorrection, exception:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   244
                    self.previous_line() # back up for another try
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   245
                    transitions = (exception.args[0],)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   246
                    if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   247
                        print >>sys.stderr, (
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   248
                              '\nStateMachine.run: TransitionCorrection to '
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   249
                              'state "%s", transition %s.'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   250
                              % (state.__class__.__name__, transitions[0]))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   251
                    continue
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   252
                except StateCorrection, exception:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   253
                    self.previous_line() # back up for another try
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   254
                    next_state = exception.args[0]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   255
                    if len(exception.args) == 1:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   256
                        transitions = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   257
                    else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   258
                        transitions = (exception.args[1],)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   259
                    if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   260
                        print >>sys.stderr, (
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   261
                              '\nStateMachine.run: StateCorrection to state '
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   262
                              '"%s", transition %s.'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   263
                              % (next_state, transitions[0]))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   264
                else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   265
                    transitions = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   266
                state = self.get_state(next_state)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   267
        except:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   268
            if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   269
                self.error()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   270
            raise
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   271
        self.observers = []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   272
        return results
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   273
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   274
    def get_state(self, next_state=None):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   275
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   276
        Return current state object; set it first if `next_state` given.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   277
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   278
        Parameter `next_state`: a string, the name of the next state.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   279
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   280
        Exception: `UnknownStateError` raised if `next_state` unknown.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   281
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   282
        if next_state:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   283
            if self.debug and next_state != self.current_state:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   284
                print >>sys.stderr, \
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   285
                      ('\nStateMachine.get_state: Changing state from '
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   286
                       '"%s" to "%s" (input line %s).'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   287
                       % (self.current_state, next_state,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   288
                          self.abs_line_number()))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   289
            self.current_state = next_state
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   290
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   291
            return self.states[self.current_state]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   292
        except KeyError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   293
            raise UnknownStateError(self.current_state)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   294
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   295
    def next_line(self, n=1):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   296
        """Load `self.line` with the `n`'th next line and return it."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   297
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   298
            try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   299
                self.line_offset += n
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   300
                self.line = self.input_lines[self.line_offset]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   301
            except IndexError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   302
                self.line = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   303
                raise EOFError
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   304
            return self.line
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   305
        finally:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   306
            self.notify_observers()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   307
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   308
    def is_next_line_blank(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   309
        """Return 1 if the next line is blank or non-existant."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   310
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   311
            return not self.input_lines[self.line_offset + 1].strip()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   312
        except IndexError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   313
            return 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   314
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   315
    def at_eof(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   316
        """Return 1 if the input is at or past end-of-file."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   317
        return self.line_offset >= len(self.input_lines) - 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   318
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   319
    def at_bof(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   320
        """Return 1 if the input is at or before beginning-of-file."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   321
        return self.line_offset <= 0
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   322
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   323
    def previous_line(self, n=1):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   324
        """Load `self.line` with the `n`'th previous line and return it."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   325
        self.line_offset -= n
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   326
        if self.line_offset < 0:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   327
            self.line = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   328
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   329
            self.line = self.input_lines[self.line_offset]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   330
        self.notify_observers()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   331
        return self.line
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   332
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   333
    def goto_line(self, line_offset):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   334
        """Jump to absolute line offset `line_offset`, load and return it."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   335
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   336
            try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   337
                self.line_offset = line_offset - self.input_offset
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   338
                self.line = self.input_lines[self.line_offset]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   339
            except IndexError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   340
                self.line = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   341
                raise EOFError
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   342
            return self.line
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   343
        finally:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   344
            self.notify_observers()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   345
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   346
    def get_source(self, line_offset):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   347
        """Return source of line at absolute line offset `line_offset`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   348
        return self.input_lines.source(line_offset - self.input_offset)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   349
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   350
    def abs_line_offset(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   351
        """Return line offset of current line, from beginning of file."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   352
        return self.line_offset + self.input_offset
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   353
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   354
    def abs_line_number(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   355
        """Return line number of current line (counting from 1)."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   356
        return self.line_offset + self.input_offset + 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   357
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   358
    def insert_input(self, input_lines, source):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   359
        self.input_lines.insert(self.line_offset + 1, '',
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   360
                                source='internal padding')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   361
        self.input_lines.insert(self.line_offset + 1, '',
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   362
                                source='internal padding')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   363
        self.input_lines.insert(self.line_offset + 2,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   364
                                StringList(input_lines, source))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   365
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   366
    def get_text_block(self, flush_left=0):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   367
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   368
        Return a contiguous block of text.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   369
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   370
        If `flush_left` is true, raise `UnexpectedIndentationError` if an
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   371
        indented line is encountered before the text block ends (with a blank
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   372
        line).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   373
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   374
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   375
            block = self.input_lines.get_text_block(self.line_offset,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   376
                                                    flush_left)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   377
            self.next_line(len(block) - 1)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   378
            return block
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   379
        except UnexpectedIndentationError, error:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   380
            block, source, lineno = error
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   381
            self.next_line(len(block) - 1) # advance to last line of block
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   382
            raise
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   383
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   384
    def check_line(self, context, state, transitions=None):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   385
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   386
        Examine one line of input for a transition match & execute its method.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   387
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   388
        Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   389
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   390
        - `context`: application-dependent storage.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   391
        - `state`: a `State` object, the current state.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   392
        - `transitions`: an optional ordered list of transition names to try,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   393
          instead of ``state.transition_order``.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   394
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   395
        Return the values returned by the transition method:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   396
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   397
        - context: possibly modified from the parameter `context`;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   398
        - next state name (`State` subclass name);
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   399
        - the result output of the transition, a list.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   400
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   401
        When there is no match, ``state.no_match()`` is called and its return
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   402
        value is returned.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   403
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   404
        if transitions is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   405
            transitions =  state.transition_order
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   406
        state_correction = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   407
        if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   408
            print >>sys.stderr, (
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   409
                  '\nStateMachine.check_line: state="%s", transitions=%r.'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   410
                  % (state.__class__.__name__, transitions))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   411
        for name in transitions:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   412
            pattern, method, next_state = state.transitions[name]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   413
            match = self.match(pattern)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   414
            if match:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   415
                if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   416
                    print >>sys.stderr, (
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   417
                          '\nStateMachine.check_line: Matched transition '
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   418
                          '"%s" in state "%s".'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   419
                          % (name, state.__class__.__name__))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   420
                return method(match, context, next_state)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   421
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   422
            if self.debug:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   423
                print >>sys.stderr, (
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   424
                      '\nStateMachine.check_line: No match in state "%s".'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   425
                      % state.__class__.__name__)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   426
            return state.no_match(context, transitions)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   427
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   428
    def match(self, pattern):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   429
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   430
        Return the result of a regular expression match.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   431
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   432
        Parameter `pattern`: an `re` compiled regular expression.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   433
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   434
        return pattern.match(self.line)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   435
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   436
    def add_state(self, state_class):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   437
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   438
        Initialize & add a `state_class` (`State` subclass) object.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   439
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   440
        Exception: `DuplicateStateError` raised if `state_class` was already
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   441
        added.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   442
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   443
        statename = state_class.__name__
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   444
        if self.states.has_key(statename):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   445
            raise DuplicateStateError(statename)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   446
        self.states[statename] = state_class(self, self.debug)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   447
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   448
    def add_states(self, state_classes):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   449
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   450
        Add `state_classes` (a list of `State` subclasses).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   451
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   452
        for state_class in state_classes:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   453
            self.add_state(state_class)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   454
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   455
    def runtime_init(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   456
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   457
        Initialize `self.states`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   458
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   459
        for state in self.states.values():
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   460
            state.runtime_init()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   461
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   462
    def error(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   463
        """Report error details."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   464
        type, value, module, line, function = _exception_data()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   465
        print >>sys.stderr, '%s: %s' % (type, value)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   466
        print >>sys.stderr, 'input line %s' % (self.abs_line_number())
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   467
        print >>sys.stderr, ('module %s, line %s, function %s'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   468
                             % (module, line, function))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   469
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   470
    def attach_observer(self, observer):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   471
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   472
        The `observer` parameter is a function or bound method which takes two
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   473
        arguments, the source and offset of the current line.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   474
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   475
        self.observers.append(observer)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   476
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   477
    def detach_observer(self, observer):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   478
        self.observers.remove(observer)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   479
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   480
    def notify_observers(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   481
        for observer in self.observers:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   482
            try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   483
                info = self.input_lines.info(self.line_offset)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   484
            except IndexError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   485
                info = (None, None)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   486
            observer(*info)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   487
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   488
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   489
class State:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   490
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   491
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   492
    State superclass. Contains a list of transitions, and transition methods.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   493
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   494
    Transition methods all have the same signature. They take 3 parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   495
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   496
    - An `re` match object. ``match.string`` contains the matched input line,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   497
      ``match.start()`` gives the start index of the match, and
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   498
      ``match.end()`` gives the end index.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   499
    - A context object, whose meaning is application-defined (initial value
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   500
      ``None``). It can be used to store any information required by the state
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   501
      machine, and the retured context is passed on to the next transition
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   502
      method unchanged.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   503
    - The name of the next state, a string, taken from the transitions list;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   504
      normally it is returned unchanged, but it may be altered by the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   505
      transition method if necessary.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   506
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   507
    Transition methods all return a 3-tuple:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   508
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   509
    - A context object, as (potentially) modified by the transition method.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   510
    - The next state name (a return value of ``None`` means no state change).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   511
    - The processing result, a list, which is accumulated by the state
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   512
      machine.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   513
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   514
    Transition methods may raise an `EOFError` to cut processing short.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   515
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   516
    There are two implicit transitions, and corresponding transition methods
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   517
    are defined: `bof()` handles the beginning-of-file, and `eof()` handles
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   518
    the end-of-file. These methods have non-standard signatures and return
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   519
    values. `bof()` returns the initial context and results, and may be used
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   520
    to return a header string, or do any other processing needed. `eof()`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   521
    should handle any remaining context and wrap things up; it returns the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   522
    final processing result.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   523
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   524
    Typical applications need only subclass `State` (or a subclass), set the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   525
    `patterns` and `initial_transitions` class attributes, and provide
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   526
    corresponding transition methods. The default object initialization will
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   527
    take care of constructing the list of transitions.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   528
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   529
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   530
    patterns = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   531
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   532
    {Name: pattern} mapping, used by `make_transition()`. Each pattern may
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   533
    be a string or a compiled `re` pattern. Override in subclasses.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   534
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   535
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   536
    initial_transitions = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   537
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   538
    A list of transitions to initialize when a `State` is instantiated.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   539
    Each entry is either a transition name string, or a (transition name, next
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   540
    state name) pair. See `make_transitions()`. Override in subclasses.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   541
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   542
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   543
    nested_sm = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   544
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   545
    The `StateMachine` class for handling nested processing.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   546
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   547
    If left as ``None``, `nested_sm` defaults to the class of the state's
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   548
    controlling state machine. Override it in subclasses to avoid the default.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   549
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   550
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   551
    nested_sm_kwargs = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   552
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   553
    Keyword arguments dictionary, passed to the `nested_sm` constructor.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   554
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   555
    Two keys must have entries in the dictionary:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   556
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   557
    - Key 'state_classes' must be set to a list of `State` classes.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   558
    - Key 'initial_state' must be set to the name of the initial state class.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   559
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   560
    If `nested_sm_kwargs` is left as ``None``, 'state_classes' defaults to the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   561
    class of the current state, and 'initial_state' defaults to the name of
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   562
    the class of the current state. Override in subclasses to avoid the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   563
    defaults.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   564
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   565
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   566
    def __init__(self, state_machine, debug=0):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   567
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   568
        Initialize a `State` object; make & add initial transitions.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   569
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   570
        Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   571
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   572
        - `statemachine`: the controlling `StateMachine` object.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   573
        - `debug`: a boolean; produce verbose output if true (nonzero).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   574
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   575
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   576
        self.transition_order = []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   577
        """A list of transition names in search order."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   578
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   579
        self.transitions = {}
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   580
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   581
        A mapping of transition names to 3-tuples containing
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   582
        (compiled_pattern, transition_method, next_state_name). Initialized as
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   583
        an instance attribute dynamically (instead of as a class attribute)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   584
        because it may make forward references to patterns and methods in this
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   585
        or other classes.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   586
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   587
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   588
        self.add_initial_transitions()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   589
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   590
        self.state_machine = state_machine
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   591
        """A reference to the controlling `StateMachine` object."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   592
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   593
        self.debug = debug
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   594
        """Debugging mode on/off."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   595
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   596
        if self.nested_sm is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   597
            self.nested_sm = self.state_machine.__class__
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   598
        if self.nested_sm_kwargs is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   599
            self.nested_sm_kwargs = {'state_classes': [self.__class__],
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   600
                                     'initial_state': self.__class__.__name__}
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   601
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   602
    def runtime_init(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   603
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   604
        Initialize this `State` before running the state machine; called from
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   605
        `self.state_machine.run()`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   606
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   607
        pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   608
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   609
    def unlink(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   610
        """Remove circular references to objects no longer required."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   611
        self.state_machine = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   612
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   613
    def add_initial_transitions(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   614
        """Make and add transitions listed in `self.initial_transitions`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   615
        if self.initial_transitions:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   616
            names, transitions = self.make_transitions(
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   617
                  self.initial_transitions)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   618
            self.add_transitions(names, transitions)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   619
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   620
    def add_transitions(self, names, transitions):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   621
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   622
        Add a list of transitions to the start of the transition list.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   623
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   624
        Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   625
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   626
        - `names`: a list of transition names.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   627
        - `transitions`: a mapping of names to transition tuples.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   628
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   629
        Exceptions: `DuplicateTransitionError`, `UnknownTransitionError`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   630
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   631
        for name in names:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   632
            if self.transitions.has_key(name):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   633
                raise DuplicateTransitionError(name)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   634
            if not transitions.has_key(name):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   635
                raise UnknownTransitionError(name)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   636
        self.transition_order[:0] = names
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   637
        self.transitions.update(transitions)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   638
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   639
    def add_transition(self, name, transition):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   640
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   641
        Add a transition to the start of the transition list.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   642
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   643
        Parameter `transition`: a ready-made transition 3-tuple.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   644
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   645
        Exception: `DuplicateTransitionError`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   646
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   647
        if self.transitions.has_key(name):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   648
            raise DuplicateTransitionError(name)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   649
        self.transition_order[:0] = [name]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   650
        self.transitions[name] = transition
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   651
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   652
    def remove_transition(self, name):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   653
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   654
        Remove a transition by `name`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   655
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   656
        Exception: `UnknownTransitionError`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   657
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   658
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   659
            del self.transitions[name]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   660
            self.transition_order.remove(name)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   661
        except:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   662
            raise UnknownTransitionError(name)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   663
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   664
    def make_transition(self, name, next_state=None):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   665
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   666
        Make & return a transition tuple based on `name`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   667
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   668
        This is a convenience function to simplify transition creation.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   669
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   670
        Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   671
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   672
        - `name`: a string, the name of the transition pattern & method. This
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   673
          `State` object must have a method called '`name`', and a dictionary
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   674
          `self.patterns` containing a key '`name`'.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   675
        - `next_state`: a string, the name of the next `State` object for this
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   676
          transition. A value of ``None`` (or absent) implies no state change
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   677
          (i.e., continue with the same state).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   678
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   679
        Exceptions: `TransitionPatternNotFound`, `TransitionMethodNotFound`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   680
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   681
        if next_state is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   682
            next_state = self.__class__.__name__
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   683
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   684
            pattern = self.patterns[name]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   685
            if not hasattr(pattern, 'match'):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   686
                pattern = re.compile(pattern)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   687
        except KeyError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   688
            raise TransitionPatternNotFound(
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   689
                  '%s.patterns[%r]' % (self.__class__.__name__, name))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   690
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   691
            method = getattr(self, name)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   692
        except AttributeError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   693
            raise TransitionMethodNotFound(
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   694
                  '%s.%s' % (self.__class__.__name__, name))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   695
        return (pattern, method, next_state)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   696
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   697
    def make_transitions(self, name_list):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   698
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   699
        Return a list of transition names and a transition mapping.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   700
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   701
        Parameter `name_list`: a list, where each entry is either a transition
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   702
        name string, or a 1- or 2-tuple (transition name, optional next state
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   703
        name).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   704
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   705
        stringtype = type('')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   706
        names = []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   707
        transitions = {}
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   708
        for namestate in name_list:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   709
            if type(namestate) is stringtype:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   710
                transitions[namestate] = self.make_transition(namestate)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   711
                names.append(namestate)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   712
            else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   713
                transitions[namestate[0]] = self.make_transition(*namestate)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   714
                names.append(namestate[0])
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   715
        return names, transitions
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   716
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   717
    def no_match(self, context, transitions):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   718
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   719
        Called when there is no match from `StateMachine.check_line()`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   720
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   721
        Return the same values returned by transition methods:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   722
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   723
        - context: unchanged;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   724
        - next state name: ``None``;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   725
        - empty result list.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   726
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   727
        Override in subclasses to catch this event.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   728
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   729
        return context, None, []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   730
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   731
    def bof(self, context):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   732
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   733
        Handle beginning-of-file. Return unchanged `context`, empty result.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   734
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   735
        Override in subclasses.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   736
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   737
        Parameter `context`: application-defined storage.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   738
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   739
        return context, []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   740
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   741
    def eof(self, context):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   742
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   743
        Handle end-of-file. Return empty result.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   744
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   745
        Override in subclasses.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   746
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   747
        Parameter `context`: application-defined storage.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   748
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   749
        return []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   750
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   751
    def nop(self, match, context, next_state):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   752
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   753
        A "do nothing" transition method.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   754
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   755
        Return unchanged `context` & `next_state`, empty result. Useful for
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   756
        simple state changes (actionless transitions).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   757
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   758
        return context, next_state, []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   759
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   760
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   761
class StateMachineWS(StateMachine):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   762
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   763
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   764
    `StateMachine` subclass specialized for whitespace recognition.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   765
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   766
    There are three methods provided for extracting indented text blocks:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   767
    
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   768
    - `get_indented()`: use when the indent is unknown.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   769
    - `get_known_indented()`: use when the indent is known for all lines.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   770
    - `get_first_known_indented()`: use when only the first line's indent is
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   771
      known.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   772
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   773
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   774
    def get_indented(self, until_blank=0, strip_indent=1):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   775
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   776
        Return a block of indented lines of text, and info.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   777
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   778
        Extract an indented block where the indent is unknown for all lines.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   779
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   780
        :Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   781
            - `until_blank`: Stop collecting at the first blank line if true
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   782
              (1).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   783
            - `strip_indent`: Strip common leading indent if true (1,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   784
              default).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   785
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   786
        :Return:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   787
            - the indented block (a list of lines of text),
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   788
            - its indent,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   789
            - its first line offset from BOF, and
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   790
            - whether or not it finished with a blank line.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   791
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   792
        offset = self.abs_line_offset()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   793
        indented, indent, blank_finish = self.input_lines.get_indented(
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   794
              self.line_offset, until_blank, strip_indent)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   795
        if indented:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   796
            self.next_line(len(indented) - 1) # advance to last indented line
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   797
        while indented and not indented[0].strip():
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   798
            indented.trim_start()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   799
            offset += 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   800
        return indented, indent, offset, blank_finish
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   801
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   802
    def get_known_indented(self, indent, until_blank=0, strip_indent=1):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   803
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   804
        Return an indented block and info.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   805
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   806
        Extract an indented block where the indent is known for all lines.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   807
        Starting with the current line, extract the entire text block with at
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   808
        least `indent` indentation (which must be whitespace, except for the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   809
        first line).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   810
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   811
        :Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   812
            - `indent`: The number of indent columns/characters.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   813
            - `until_blank`: Stop collecting at the first blank line if true
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   814
              (1).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   815
            - `strip_indent`: Strip `indent` characters of indentation if true
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   816
              (1, default).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   817
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   818
        :Return:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   819
            - the indented block,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   820
            - its first line offset from BOF, and
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   821
            - whether or not it finished with a blank line.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   822
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   823
        offset = self.abs_line_offset()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   824
        indented, indent, blank_finish = self.input_lines.get_indented(
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   825
              self.line_offset, until_blank, strip_indent,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   826
              block_indent=indent)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   827
        self.next_line(len(indented) - 1) # advance to last indented line
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   828
        while indented and not indented[0].strip():
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   829
            indented.trim_start()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   830
            offset += 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   831
        return indented, offset, blank_finish
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   832
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   833
    def get_first_known_indented(self, indent, until_blank=0, strip_indent=1,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   834
                                 strip_top=1):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   835
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   836
        Return an indented block and info.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   837
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   838
        Extract an indented block where the indent is known for the first line
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   839
        and unknown for all other lines.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   840
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   841
        :Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   842
            - `indent`: The first line's indent (# of columns/characters).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   843
            - `until_blank`: Stop collecting at the first blank line if true
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   844
              (1).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   845
            - `strip_indent`: Strip `indent` characters of indentation if true
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   846
              (1, default).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   847
            - `strip_top`: Strip blank lines from the beginning of the block.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   848
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   849
        :Return:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   850
            - the indented block,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   851
            - its indent,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   852
            - its first line offset from BOF, and
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   853
            - whether or not it finished with a blank line.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   854
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   855
        offset = self.abs_line_offset()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   856
        indented, indent, blank_finish = self.input_lines.get_indented(
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   857
              self.line_offset, until_blank, strip_indent,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   858
              first_indent=indent)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   859
        self.next_line(len(indented) - 1) # advance to last indented line
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   860
        if strip_top:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   861
            while indented and not indented[0].strip():
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   862
                indented.trim_start()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   863
                offset += 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   864
        return indented, indent, offset, blank_finish
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   865
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   866
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   867
class StateWS(State):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   868
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   869
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   870
    State superclass specialized for whitespace (blank lines & indents).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   871
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   872
    Use this class with `StateMachineWS`.  The transitions 'blank' (for blank
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   873
    lines) and 'indent' (for indented text blocks) are added automatically,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   874
    before any other transitions.  The transition method `blank()` handles
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   875
    blank lines and `indent()` handles nested indented blocks.  Indented
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   876
    blocks trigger a new state machine to be created by `indent()` and run.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   877
    The class of the state machine to be created is in `indent_sm`, and the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   878
    constructor keyword arguments are in the dictionary `indent_sm_kwargs`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   879
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   880
    The methods `known_indent()` and `firstknown_indent()` are provided for
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   881
    indented blocks where the indent (all lines' and first line's only,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   882
    respectively) is known to the transition method, along with the attributes
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   883
    `known_indent_sm` and `known_indent_sm_kwargs`.  Neither transition method
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   884
    is triggered automatically.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   885
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   886
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   887
    indent_sm = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   888
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   889
    The `StateMachine` class handling indented text blocks.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   890
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   891
    If left as ``None``, `indent_sm` defaults to the value of
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   892
    `State.nested_sm`.  Override it in subclasses to avoid the default.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   893
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   894
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   895
    indent_sm_kwargs = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   896
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   897
    Keyword arguments dictionary, passed to the `indent_sm` constructor.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   898
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   899
    If left as ``None``, `indent_sm_kwargs` defaults to the value of
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   900
    `State.nested_sm_kwargs`. Override it in subclasses to avoid the default.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   901
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   902
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   903
    known_indent_sm = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   904
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   905
    The `StateMachine` class handling known-indented text blocks.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   906
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   907
    If left as ``None``, `known_indent_sm` defaults to the value of
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   908
    `indent_sm`.  Override it in subclasses to avoid the default.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   909
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   910
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   911
    known_indent_sm_kwargs = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   912
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   913
    Keyword arguments dictionary, passed to the `known_indent_sm` constructor.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   914
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   915
    If left as ``None``, `known_indent_sm_kwargs` defaults to the value of
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   916
    `indent_sm_kwargs`. Override it in subclasses to avoid the default.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   917
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   918
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   919
    ws_patterns = {'blank': ' *$',
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   920
                   'indent': ' +'}
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   921
    """Patterns for default whitespace transitions.  May be overridden in
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   922
    subclasses."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   923
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   924
    ws_initial_transitions = ('blank', 'indent')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   925
    """Default initial whitespace transitions, added before those listed in
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   926
    `State.initial_transitions`.  May be overridden in subclasses."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   927
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   928
    def __init__(self, state_machine, debug=0):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   929
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   930
        Initialize a `StateSM` object; extends `State.__init__()`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   931
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   932
        Check for indent state machine attributes, set defaults if not set.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   933
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   934
        State.__init__(self, state_machine, debug)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   935
        if self.indent_sm is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   936
            self.indent_sm = self.nested_sm
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   937
        if self.indent_sm_kwargs is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   938
            self.indent_sm_kwargs = self.nested_sm_kwargs
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   939
        if self.known_indent_sm is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   940
            self.known_indent_sm = self.indent_sm
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   941
        if self.known_indent_sm_kwargs is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   942
            self.known_indent_sm_kwargs = self.indent_sm_kwargs
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   943
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   944
    def add_initial_transitions(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   945
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   946
        Add whitespace-specific transitions before those defined in subclass.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   947
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   948
        Extends `State.add_initial_transitions()`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   949
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   950
        State.add_initial_transitions(self)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   951
        if self.patterns is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   952
            self.patterns = {}
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   953
        self.patterns.update(self.ws_patterns)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   954
        names, transitions = self.make_transitions(
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   955
            self.ws_initial_transitions)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   956
        self.add_transitions(names, transitions)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   957
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   958
    def blank(self, match, context, next_state):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   959
        """Handle blank lines. Does nothing. Override in subclasses."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   960
        return self.nop(match, context, next_state)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   961
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   962
    def indent(self, match, context, next_state):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   963
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   964
        Handle an indented text block. Extend or override in subclasses.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   965
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   966
        Recursively run the registered state machine for indented blocks
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   967
        (`self.indent_sm`).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   968
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   969
        indented, indent, line_offset, blank_finish = \
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   970
              self.state_machine.get_indented()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   971
        sm = self.indent_sm(debug=self.debug, **self.indent_sm_kwargs)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   972
        results = sm.run(indented, input_offset=line_offset)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   973
        return context, next_state, results
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   974
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   975
    def known_indent(self, match, context, next_state):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   976
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   977
        Handle a known-indent text block. Extend or override in subclasses.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   978
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   979
        Recursively run the registered state machine for known-indent indented
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   980
        blocks (`self.known_indent_sm`). The indent is the length of the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   981
        match, ``match.end()``.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   982
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   983
        indented, line_offset, blank_finish = \
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   984
              self.state_machine.get_known_indented(match.end())
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   985
        sm = self.known_indent_sm(debug=self.debug,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   986
                                 **self.known_indent_sm_kwargs)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   987
        results = sm.run(indented, input_offset=line_offset)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   988
        return context, next_state, results
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   989
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   990
    def first_known_indent(self, match, context, next_state):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   991
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   992
        Handle an indented text block (first line's indent known).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   993
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   994
        Extend or override in subclasses.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   995
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   996
        Recursively run the registered state machine for known-indent indented
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   997
        blocks (`self.known_indent_sm`). The indent is the length of the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   998
        match, ``match.end()``.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
   999
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1000
        indented, line_offset, blank_finish = \
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1001
              self.state_machine.get_first_known_indented(match.end())
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1002
        sm = self.known_indent_sm(debug=self.debug,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1003
                                 **self.known_indent_sm_kwargs)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1004
        results = sm.run(indented, input_offset=line_offset)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1005
        return context, next_state, results
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1006
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1007
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1008
class _SearchOverride:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1009
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1010
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1011
    Mix-in class to override `StateMachine` regular expression behavior.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1012
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1013
    Changes regular expression matching, from the default `re.match()`
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1014
    (succeeds only if the pattern matches at the start of `self.line`) to
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1015
    `re.search()` (succeeds if the pattern matches anywhere in `self.line`).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1016
    When subclassing a `StateMachine`, list this class **first** in the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1017
    inheritance list of the class definition.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1018
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1019
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1020
    def match(self, pattern):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1021
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1022
        Return the result of a regular expression search.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1023
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1024
        Overrides `StateMachine.match()`.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1025
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1026
        Parameter `pattern`: `re` compiled regular expression.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1027
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1028
        return pattern.search(self.line)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1029
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1030
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1031
class SearchStateMachine(_SearchOverride, StateMachine):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1032
    """`StateMachine` which uses `re.search()` instead of `re.match()`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1033
    pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1034
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1035
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1036
class SearchStateMachineWS(_SearchOverride, StateMachineWS):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1037
    """`StateMachineWS` which uses `re.search()` instead of `re.match()`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1038
    pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1039
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1040
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1041
class ViewList:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1042
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1043
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1044
    List with extended functionality: slices of ViewList objects are child
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1045
    lists, linked to their parents. Changes made to a child list also affect
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1046
    the parent list.  A child list is effectively a "view" (in the SQL sense)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1047
    of the parent list.  Changes to parent lists, however, do *not* affect
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1048
    active child lists.  If a parent list is changed, any active child lists
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1049
    should be recreated.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1050
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1051
    The start and end of the slice can be trimmed using the `trim_start()` and
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1052
    `trim_end()` methods, without affecting the parent list.  The link between
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1053
    child and parent lists can be broken by calling `disconnect()` on the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1054
    child list.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1055
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1056
    Also, ViewList objects keep track of the source & offset of each item. 
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1057
    This information is accessible via the `source()`, `offset()`, and
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1058
    `info()` methods.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1059
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1060
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1061
    def __init__(self, initlist=None, source=None, items=None,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1062
                 parent=None, parent_offset=None):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1063
        self.data = []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1064
        """The actual list of data, flattened from various sources."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1065
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1066
        self.items = []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1067
        """A list of (source, offset) pairs, same length as `self.data`: the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1068
        source of each line and the offset of each line from the beginning of
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1069
        its source."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1070
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1071
        self.parent = parent
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1072
        """The parent list."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1073
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1074
        self.parent_offset = parent_offset
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1075
        """Offset of this list from the beginning of the parent list."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1076
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1077
        if isinstance(initlist, ViewList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1078
            self.data = initlist.data[:]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1079
            self.items = initlist.items[:]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1080
        elif initlist is not None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1081
            self.data = list(initlist)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1082
            if items:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1083
                self.items = items
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1084
            else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1085
                self.items = [(source, i) for i in range(len(initlist))]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1086
        assert len(self.data) == len(self.items), 'data mismatch'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1087
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1088
    def __str__(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1089
        return str(self.data)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1090
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1091
    def __repr__(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1092
        return '%s(%s, items=%s)' % (self.__class__.__name__,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1093
                                     self.data, self.items)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1094
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1095
    def __lt__(self, other): return self.data <  self.__cast(other)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1096
    def __le__(self, other): return self.data <= self.__cast(other)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1097
    def __eq__(self, other): return self.data == self.__cast(other)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1098
    def __ne__(self, other): return self.data != self.__cast(other)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1099
    def __gt__(self, other): return self.data >  self.__cast(other)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1100
    def __ge__(self, other): return self.data >= self.__cast(other)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1101
    def __cmp__(self, other): return cmp(self.data, self.__cast(other))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1102
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1103
    def __cast(self, other):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1104
        if isinstance(other, ViewList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1105
            return other.data
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1106
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1107
            return other
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1108
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1109
    def __contains__(self, item): return item in self.data
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1110
    def __len__(self): return len(self.data)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1111
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1112
    # The __getitem__()/__setitem__() methods check whether the index
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1113
    # is a slice first, since native list objects start supporting
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1114
    # them directly in Python 2.3 (no exception is raised when
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1115
    # indexing a list with a slice object; they just work).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1116
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1117
    def __getitem__(self, i):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1118
        if isinstance(i, types.SliceType):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1119
            assert i.step in (None, 1),  'cannot handle slice with stride'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1120
            return self.__class__(self.data[i.start:i.stop],
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1121
                                  items=self.items[i.start:i.stop],
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1122
                                  parent=self, parent_offset=i.start)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1123
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1124
            return self.data[i]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1125
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1126
    def __setitem__(self, i, item):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1127
        if isinstance(i, types.SliceType):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1128
            assert i.step in (None, 1), 'cannot handle slice with stride'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1129
            if not isinstance(item, ViewList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1130
                raise TypeError('assigning non-ViewList to ViewList slice')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1131
            self.data[i.start:i.stop] = item.data
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1132
            self.items[i.start:i.stop] = item.items
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1133
            assert len(self.data) == len(self.items), 'data mismatch'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1134
            if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1135
                self.parent[i.start + self.parent_offset
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1136
                            : i.stop + self.parent_offset] = item
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1137
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1138
            self.data[i] = item
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1139
            if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1140
                self.parent[i + self.parent_offset] = item
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1141
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1142
    def __delitem__(self, i):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1143
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1144
            del self.data[i]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1145
            del self.items[i]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1146
            if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1147
                del self.parent[i + self.parent_offset]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1148
        except TypeError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1149
            assert i.step is None, 'cannot handle slice with stride'
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1150
            del self.data[i.start:i.stop]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1151
            del self.items[i.start:i.stop]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1152
            if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1153
                del self.parent[i.start + self.parent_offset
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1154
                                : i.stop + self.parent_offset]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1155
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1156
    def __add__(self, other):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1157
        if isinstance(other, ViewList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1158
            return self.__class__(self.data + other.data,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1159
                                  items=(self.items + other.items))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1160
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1161
            raise TypeError('adding non-ViewList to a ViewList')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1162
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1163
    def __radd__(self, other):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1164
        if isinstance(other, ViewList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1165
            return self.__class__(other.data + self.data,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1166
                                  items=(other.items + self.items))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1167
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1168
            raise TypeError('adding ViewList to a non-ViewList')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1169
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1170
    def __iadd__(self, other):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1171
        if isinstance(other, ViewList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1172
            self.data += other.data
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1173
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1174
            raise TypeError('argument to += must be a ViewList')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1175
        return self
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1176
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1177
    def __mul__(self, n):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1178
        return self.__class__(self.data * n, items=(self.items * n))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1179
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1180
    __rmul__ = __mul__
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1181
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1182
    def __imul__(self, n):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1183
        self.data *= n
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1184
        self.items *= n
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1185
        return self
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1186
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1187
    def extend(self, other):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1188
        if not isinstance(other, ViewList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1189
            raise TypeError('extending a ViewList with a non-ViewList')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1190
        if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1191
            self.parent.insert(len(self.data) + self.parent_offset, other)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1192
        self.data.extend(other.data)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1193
        self.items.extend(other.items)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1194
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1195
    def append(self, item, source=None, offset=0):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1196
        if source is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1197
            self.extend(item)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1198
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1199
            if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1200
                self.parent.insert(len(self.data) + self.parent_offset, item,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1201
                                   source, offset)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1202
            self.data.append(item)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1203
            self.items.append((source, offset))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1204
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1205
    def insert(self, i, item, source=None, offset=0):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1206
        if source is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1207
            if not isinstance(item, ViewList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1208
                raise TypeError('inserting non-ViewList with no source given')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1209
            self.data[i:i] = item.data
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1210
            self.items[i:i] = item.items
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1211
            if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1212
                index = (len(self.data) + i) % len(self.data)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1213
                self.parent.insert(index + self.parent_offset, item)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1214
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1215
            self.data.insert(i, item)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1216
            self.items.insert(i, (source, offset))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1217
            if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1218
                index = (len(self.data) + i) % len(self.data)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1219
                self.parent.insert(index + self.parent_offset, item,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1220
                                   source, offset)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1221
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1222
    def pop(self, i=-1):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1223
        if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1224
            index = (len(self.data) + i) % len(self.data)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1225
            self.parent.pop(index + self.parent_offset)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1226
        self.items.pop(i)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1227
        return self.data.pop(i)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1228
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1229
    def trim_start(self, n=1):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1230
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1231
        Remove items from the start of the list, without touching the parent.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1232
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1233
        if n > len(self.data):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1234
            raise IndexError("Size of trim too large; can't trim %s items "
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1235
                             "from a list of size %s." % (n, len(self.data)))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1236
        elif n < 0:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1237
            raise IndexError('Trim size must be >= 0.')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1238
        del self.data[:n]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1239
        del self.items[:n]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1240
        if self.parent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1241
            self.parent_offset += n
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1242
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1243
    def trim_end(self, n=1):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1244
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1245
        Remove items from the end of the list, without touching the parent.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1246
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1247
        if n > len(self.data):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1248
            raise IndexError("Size of trim too large; can't trim %s items "
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1249
                             "from a list of size %s." % (n, len(self.data)))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1250
        elif n < 0:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1251
            raise IndexError('Trim size must be >= 0.')
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1252
        del self.data[-n:]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1253
        del self.items[-n:]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1254
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1255
    def remove(self, item):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1256
        index = self.index(item)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1257
        del self[index]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1258
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1259
    def count(self, item): return self.data.count(item)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1260
    def index(self, item): return self.data.index(item)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1261
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1262
    def reverse(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1263
        self.data.reverse()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1264
        self.items.reverse()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1265
        self.parent = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1266
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1267
    def sort(self, *args):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1268
        tmp = zip(self.data, self.items)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1269
        tmp.sort(*args)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1270
        self.data = [entry[0] for entry in tmp]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1271
        self.items = [entry[1] for entry in tmp]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1272
        self.parent = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1273
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1274
    def info(self, i):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1275
        """Return source & offset for index `i`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1276
        try:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1277
            return self.items[i]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1278
        except IndexError:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1279
            if i == len(self.data):     # Just past the end
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1280
                return self.items[i - 1][0], None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1281
            else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1282
                raise
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1283
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1284
    def source(self, i):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1285
        """Return source for index `i`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1286
        return self.info(i)[0]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1287
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1288
    def offset(self, i):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1289
        """Return offset for index `i`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1290
        return self.info(i)[1]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1291
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1292
    def disconnect(self):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1293
        """Break link between this list and parent list."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1294
        self.parent = None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1295
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1296
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1297
class StringList(ViewList):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1298
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1299
    """A `ViewList` with string-specific methods."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1300
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1301
    def trim_left(self, length, start=0, end=sys.maxint):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1302
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1303
        Trim `length` characters off the beginning of each item, in-place,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1304
        from index `start` to `end`.  No whitespace-checking is done on the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1305
        trimmed text.  Does not affect slice parent.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1306
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1307
        self.data[start:end] = [line[length:]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1308
                                for line in self.data[start:end]]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1309
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1310
    def get_text_block(self, start, flush_left=0):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1311
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1312
        Return a contiguous block of text.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1313
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1314
        If `flush_left` is true, raise `UnexpectedIndentationError` if an
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1315
        indented line is encountered before the text block ends (with a blank
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1316
        line).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1317
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1318
        end = start
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1319
        last = len(self.data)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1320
        while end < last:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1321
            line = self.data[end]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1322
            if not line.strip():
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1323
                break
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1324
            if flush_left and (line[0] == ' '):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1325
                source, offset = self.info(end)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1326
                raise UnexpectedIndentationError(self[start:end], source,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1327
                                                 offset + 1)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1328
            end += 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1329
        return self[start:end]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1330
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1331
    def get_indented(self, start=0, until_blank=0, strip_indent=1,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1332
                     block_indent=None, first_indent=None):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1333
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1334
        Extract and return a StringList of indented lines of text.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1335
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1336
        Collect all lines with indentation, determine the minimum indentation,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1337
        remove the minimum indentation from all indented lines (unless
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1338
        `strip_indent` is false), and return them. All lines up to but not
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1339
        including the first unindented line will be returned.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1340
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1341
        :Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1342
          - `start`: The index of the first line to examine.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1343
          - `until_blank`: Stop collecting at the first blank line if true.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1344
          - `strip_indent`: Strip common leading indent if true (default).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1345
          - `block_indent`: The indent of the entire block, if known.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1346
          - `first_indent`: The indent of the first line, if known.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1347
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1348
        :Return:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1349
          - a StringList of indented lines with mininum indent removed;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1350
          - the amount of the indent;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1351
          - a boolean: did the indented block finish with a blank line or EOF?
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1352
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1353
        indent = block_indent           # start with None if unknown
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1354
        end = start
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1355
        if block_indent is not None and first_indent is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1356
            first_indent = block_indent
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1357
        if first_indent is not None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1358
            end += 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1359
        last = len(self.data)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1360
        while end < last:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1361
            line = self.data[end]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1362
            if line and (line[0] != ' '
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1363
                         or (block_indent is not None
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1364
                             and line[:block_indent].strip())):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1365
                # Line not indented or insufficiently indented.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1366
                # Block finished properly iff the last indented line blank:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1367
                blank_finish = ((end > start)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1368
                                and not self.data[end - 1].strip())
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1369
                break
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1370
            stripped = line.lstrip()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1371
            if not stripped:            # blank line
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1372
                if until_blank:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1373
                    blank_finish = 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1374
                    break
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1375
            elif block_indent is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1376
                line_indent = len(line) - len(stripped)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1377
                if indent is None:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1378
                    indent = line_indent
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1379
                else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1380
                    indent = min(indent, line_indent)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1381
            end += 1
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1382
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1383
            blank_finish = 1            # block ends at end of lines
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1384
        block = self[start:end]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1385
        if first_indent is not None and block:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1386
            block.data[0] = block.data[0][first_indent:]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1387
        if indent and strip_indent:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1388
            block.trim_left(indent, start=(first_indent is not None))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1389
        return block, indent or 0, blank_finish
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1390
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1391
    def get_2D_block(self, top, left, bottom, right, strip_indent=1):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1392
        block = self[top:bottom]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1393
        indent = right
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1394
        for i in range(len(block.data)):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1395
            block.data[i] = line = block.data[i][left:right].rstrip()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1396
            if line:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1397
                indent = min(indent, len(line) - len(line.lstrip()))
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1398
        if strip_indent and 0 < indent < right:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1399
            block.data = [line[indent:] for line in block.data]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1400
        return block
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1401
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1402
    def pad_double_width(self, pad_char):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1403
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1404
        Pad all double-width characters in self by appending `pad_char` to each.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1405
        For East Asian language support.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1406
        """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1407
        if hasattr(unicodedata, 'east_asian_width'):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1408
            east_asian_width = unicodedata.east_asian_width
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1409
        else:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1410
            return                      # new in Python 2.4
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1411
        for i in range(len(self.data)):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1412
            line = self.data[i]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1413
            if isinstance(line, types.UnicodeType):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1414
                new = []
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1415
                for char in line:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1416
                    new.append(char)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1417
                    if east_asian_width(char) in 'WF': # 'W'ide & 'F'ull-width
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1418
                        new.append(pad_char)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1419
                self.data[i] = ''.join(new)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1420
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1421
    def replace(self, old, new):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1422
        """Replace all occurrences of substring `old` with `new`."""
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1423
        for i in range(len(self.data)):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1424
            self.data[i] = self.data[i].replace(old, new)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1425
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1426
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1427
class StateMachineError(Exception): pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1428
class UnknownStateError(StateMachineError): pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1429
class DuplicateStateError(StateMachineError): pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1430
class UnknownTransitionError(StateMachineError): pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1431
class DuplicateTransitionError(StateMachineError): pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1432
class TransitionPatternNotFound(StateMachineError): pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1433
class TransitionMethodNotFound(StateMachineError): pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1434
class UnexpectedIndentationError(StateMachineError): pass
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1435
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1436
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1437
class TransitionCorrection(Exception):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1438
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1439
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1440
    Raise from within a transition method to switch to another transition.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1441
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1442
    Raise with one argument, the new transition name.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1443
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1444
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1445
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1446
class StateCorrection(Exception):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1447
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1448
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1449
    Raise from within a transition method to switch to another state.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1450
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1451
    Raise with one or two arguments: new state name, and an optional new
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1452
    transition name.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1453
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1454
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1455
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1456
def string2lines(astring, tab_width=8, convert_whitespace=0,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1457
                 whitespace=re.compile('[\v\f]')):
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1458
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1459
    Return a list of one-line strings with tabs expanded, no newlines, and
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1460
    trailing whitespace stripped.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1461
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1462
    Each tab is expanded with between 1 and `tab_width` spaces, so that the
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1463
    next character's index becomes a multiple of `tab_width` (8 by default).
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1464
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1465
    Parameters:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1466
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1467
    - `astring`: a multi-line string.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1468
    - `tab_width`: the number of columns between tab stops.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1469
    - `convert_whitespace`: convert form feeds and vertical tabs to spaces?
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1470
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1471
    if convert_whitespace:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1472
        astring = whitespace.sub(' ', astring)
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1473
    return [s.expandtabs(tab_width).rstrip() for s in astring.splitlines()]
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1474
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1475
def _exception_data():
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1476
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1477
    Return exception information:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1478
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1479
    - the exception's class name;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1480
    - the exception object;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1481
    - the name of the file containing the offending code;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1482
    - the line number of the offending code;
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1483
    - the function name of the offending code.
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1484
    """
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1485
    type, value, traceback = sys.exc_info()
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1486
    while traceback.tb_next:
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1487
        traceback = traceback.tb_next
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1488
    code = traceback.tb_frame.f_code
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1489
    return (type.__name__, value, code.co_filename, traceback.tb_lineno,
d8ac696cc51f helium_7.0-r14027
wbernard
parents:
diff changeset
  1490
            code.co_name)