Package libxyz :: Package parser :: Module flat |
[hide private]
[frames] | no frames]

# Source Code for Module libxyz.parser.flat

      1  #-*- coding: utf8 -*
      2  #
      3  # Max E. Kuznecov ~syhpoon <syhpoon@syhpoon.name> 2008
      4  #
      5  # This file is part of XYZCommander.
      6  # XYZCommander is free software: you can redistribute it and/or modify
      7  # it under the terms of the GNU Lesser Public License as published by
      8  # the Free Software Foundation, either version 3 of the License, or
      9  # (at your option) any later version.
     10  # XYZCommander is distributed in the hope that it will be useful,
     11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     13  # GNU Lesser Public License for more details.
     14  # You should have received a copy of the GNU Lesser Public License
     15  # along with XYZCommander. If not, see <http://www.gnu.org/licenses/>.
     16
     17  from libxyz.parser import Lexer
     18  from libxyz.parser import BaseParser
     19  from libxyz.parser import ParsedData
     20
     21  from libxyz.exceptions import XYZValueError
     22  from libxyz.exceptions import ParseError
     23  from libxyz.exceptions import LexerError
     24
     25 -class FlatParser(BaseParser):
     26      """
     27      FlatParser is simple linear parser.
     28
     29      Format:
     30
     31      var1 <assign> val1 <delimiter>
     32      var2 <assign> val2 <delimiter>
     33      ...
     34      """
     35
     36      STATE_VARIABLE = 0
     37      STATE_ASSIGN = 1
     38      STATE_VALUE = 2
     39      STATE_LIST_VALUE = 3
     40      STATE_DELIM = 4
     41
     42      DEFAULT_OPT = {
     43                     u"comment": u"#",
     44                     u"assignchar": u":",
     45                     u"delimiter": u"\n",
     46                     u"validvars": (),
     47                     u"value_validator": None,
     48                     u"count": 0,
     49                     u"list_separator": u",",
     50                     }
     51
     52 -    def __init__(self, opt=None):
     53          """
     54          @param opt: Options
     55          @type opt: dict
     56
     57          Available options:
     58              - comment: Comment character.
     59                Everything else ignored until EOL.
     60                Type: I{string (single char)}
     61              - assignchar: Variable-value split character.
     62                Type: I{string (single char)}
     63              - delimiter: Character to use as delimiter between statements.
     64                Type: I{string (single char)}
     65              - validvars: List of variables valid within block.
     66                Type: I{sequence}
     67              - value_validator: Value validator
     68                Type: A function that takes two args:
     69                variable and value and validates them.
     70                In case value is invalid, XYZValueError must be raised.
     71                Otherwise function must return required value, possibly modified.
     72              - count: How many blocks to parse. If count <= 0 - will parse
     73                all available.
     74                Type: integer
     75              - list_separator: Character to separate elements in list
     76                Type: I{string (single char)}
     77                Default: ,
     78          """
     79
     80          super(FlatParser, self).__init__()
     81
     82          self._parsed = 0
     83          self._result = ParsedData()
     84          self._current_list = []
     85          self._lexer = None
     86          self._state = self.STATE_VARIABLE
     87
     88          self.opt = opt or self.DEFAULT_OPT
     89          self.set_opt(self.DEFAULT_OPT, self.opt)
     90
     91          self._parse_table = {
     92              self.STATE_VARIABLE: self._process_state_variable,
     93              self.STATE_ASSIGN: self._process_state_assign,
     94              self.STATE_VALUE: self._process_state_value,
     95              self.STATE_LIST_VALUE: self._process_state_list_value,
     96              self.STATE_DELIM: self._process_state_delim,
     97          }
     98
     99      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    100
    101 -    def parse(self, source, default_data=None):
    102          """
    103          Begin parsing
    104          @param default_data: Dictionary with default values.
    105          """
    106
    107          self._cleanup()
    108
    109          if default_data and isinstance(default_data, dict):
    110              self._result = default_data.copy()
    111
    112          _tokens = (
    113                     self.assignchar,
    114                     self.delimiter,
    115                     self.list_separator
    116                    )
    117
    118          self._lexer = Lexer(source, _tokens, self.comment, macro=None)
    119
    120          try:
    121              while True:
    122                  _res = self._lexer.lexer()
    123
    124                  if _res is None:
    125                      break
    126                  else:
    127                      _lex, _val = _res
    128
    129                  if _val == u"\n" and self._state not in \
    130                  (self.STATE_DELIM, self.STATE_LIST_VALUE):
    131                      continue
    132                  self._parse_table[self._state](_val)
    133          except LexerError, e:
    134              self.error(str(e))
    135
    136          # Finish assembling value
    137          if self._state == self.STATE_LIST_VALUE:
    138              self._process_state_list_value(None)
    139
    140          self._check_complete()
    141
    142          return self._result
    143
    144      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    145
    146 -    def _process_state_variable(self, word):
    147          if self.count > 0 and self.count == self._parsed:
    148              self._lexer.done()
    149              return
    150
    151          if self.validvars and word not in self.validvars:
    152                  self.error(_(u"Unknown variable %s" % word))
    153          else:
    154              self._varname = word
    155              self._state = self.STATE_ASSIGN
    156
    157      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    158
    159 -    def _process_state_assign(self, word):
    160          if word != self.assignchar:
    161              self.error(msg=(word, self.assignchar),
    162                         etype=self.error_unexpected)
    163          else:
    164              self._state = self.STATE_VALUE
    165              self._lexer.escaping_on()
    166
    167      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    168
    169 -    def _process_state_value(self, word):
    170          self._current_list.append(word)
    171          self._state = self.STATE_LIST_VALUE
    172
    173      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    174
    175 -    def _process_state_list_value(self, word):
    176          if word == self.list_separator:
    177              self._state = self.STATE_VALUE
    178              return
    179
    180          if len(self._current_list) == 1:
    181              _value = self._current_list[0]
    182          else:
    183              _value = tuple(self._current_list)
    184
    185          if self.value_validator:
    186              try:
    187                  _value = self.value_validator(self._varname, _value)
    188              except XYZValueError, e:
    189                  self.error(_(u"Invalid value: %s" % str(e)))
    190
    191          self._result[self._varname] = _value
    192          self._parsed += 1
    193          self._varname = None
    194
    195          self._current_list = []
    196          self._lexer.escaping_off()
    197          self._state = self.STATE_DELIM
    198
    199          if word is not None:
    200              self._lexer.unget(word)
    201
    202      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    203
    204 -    def _process_state_delim(self, word):
    205          if self.count > 0 and self.count == self._parsed:
    206              self._lexer.done()
    207              return
    208
    209          if word != self.delimiter:
    210              self.error(msg=(word, self.delimiter),
    211                         etype=self.error_unexpected)
    212          else:
    213              self._state = self.STATE_VARIABLE
    214
    215      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    216
    217 -    def _cleanup(self):
    218          self._parsed = 0
    219          self._state = self.STATE_VARIABLE
    220          self._varname = None
    221          self._result = ParsedData()
    222
    223      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    224
    225 -    def _check_complete(self):
    226          if self._state not in (self.STATE_VARIABLE, self.STATE_DELIM):
    227              self.error(_(u"Unterminated expression"))
    228