Package lepl :: Package bin :: Module literal
[hide private]
[frames] | no frames]

Source Code for Module lepl.bin.literal

  1   
  2  # The contents of this file are subject to the Mozilla Public License 
  3  # (MPL) Version 1.1 (the "License"); you may not use this file except 
  4  # in compliance with the License. You may obtain a copy of the License 
  5  # at http://www.mozilla.org/MPL/ 
  6  # 
  7  # Software distributed under the License is distributed on an "AS IS" 
  8  # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
  9  # the License for the specific language governing rights and 
 10  # limitations under the License. 
 11  # 
 12  # The Original Code is LEPL (http://www.acooke.org/lepl) 
 13  # The Initial Developer of the Original Code is Andrew Cooke. 
 14  # Portions created by the Initial Developer are Copyright (C) 2009-2010 
 15  # Andrew Cooke (andrew@acooke.org). All Rights Reserved. 
 16  # 
 17  # Alternatively, the contents of this file may be used under the terms 
 18  # of the LGPL license (the GNU Lesser General Public License, 
 19  # http://www.gnu.org/licenses/lgpl.html), in which case the provisions 
 20  # of the LGPL License are applicable instead of those above. 
 21  # 
 22  # If you wish to allow use of your version of this file only under the 
 23  # terms of the LGPL License and not to allow others to use your version 
 24  # of this file under the MPL, indicate your decision by deleting the 
 25  # provisions above and replace them with the notice and other provisions 
 26  # required by the LGPL License.  If you do not delete the provisions 
 27  # above, a recipient may use your version of this file under either the 
 28  # MPL or the LGPL License. 
 29   
 30  ''' 
 31  Specify and construct binary structures. 
 32   
 33  This is necessary for tests and may be useful in its own right.  Note that it 
 34  is also quite easy to construct `Node` instances with `BitString` data directly  
 35  in Python. 
 36   
 37  The construction of binary values is a two-stage process.  First, we describe 
 38  a Python structure.  Then we encode that structure as a binary value.  As is 
 39  standard in LEPL, the Python construct consists of `Node` instances. 
 40   
 41  The description of values has the form: 
 42    Node(byte=0xff/8, 0*100, Node(...), (...)) 
 43     
 44  In more detail: 
 45    () is used for grouping, must exist outside the entire description, and 
 46       defines a Node.  If preceded by a name, then that is used to create  
 47       a subclass of Node (unless it is "Node", in which case it is the  
 48       default).  For now, repeated names are not validated in any way for  
 49       consistency. 
 50    name=value/length is used for defining a value, in various ways: 
 51      value anonymous value (byte or array) 
 52      value/length anonymous value with specified length 
 53      name=value named byte or array 
 54      name=value/length named value with given length 
 55    * repeats a value, so a*b repeats 'a', b number of times. 
 56  ''' 
 57   
 58  if bytes is str: 
 59      print('Binary parsing unsupported in this Python version') 
 60  else:     
 61       
62 - def make_binary_parser():
63 ''' 64 Create a parser for binary data. 65 ''' 66 67 # avoid import loops 68 from lepl import Word, Letter, Digit, UnsignedInteger, \ 69 Regexp, DfaRegexp, Drop, Separator, Delayed, Optional, Any, First, \ 70 args, Trace, TraceVariables 71 from lepl.bin.bits import BitString 72 from lepl.support.node import Node 73 74 classes = {} 75 76 def named_class(name, *args): 77 ''' 78 Given a name and some args, create a sub-class of Binary and 79 create an instance with the given content. 80 ''' 81 if name not in classes: 82 classes[name] = type(name, (Node,), {}) 83 return classes[name](*args)
84 85 with TraceVariables(False): 86 87 mult = lambda l, n: BitString.from_sequence([l] * int(n, 0)) 88 89 # an attribute or class name 90 name = Word(Letter(), Letter() | Digit() | '_') 91 92 # lengths can be integers (bits) or floats (bytes.bits) 93 # but if we have a float, we do not want to parse as an int 94 # (or we will get a conversion error due to too small length) 95 length = First(UnsignedInteger() + '.' + Optional(UnsignedInteger()), 96 UnsignedInteger()) 97 98 # a literal decimal 99 decimal = UnsignedInteger() 100 101 # a binary number (without pre/postfix) 102 binary = Any('01')[1:] 103 104 # an octal number (without pre/postfix) 105 octal = Any('01234567')[1:] 106 107 # a hex number (without pre/postfix) 108 hex_ = Regexp('[a-fA-F0-9]')[1:] 109 110 # the letters used for binary, octal and hex values 111 #(eg the 'x' in 0xffee) 112 # pylint: disable-msg=C0103 113 b, o, x, d = Any('bB'), Any('oO'), Any('xX'), Any('dD') 114 115 # a decimal with optional pre/postfix 116 dec = '0' + d + decimal | decimal + d + '0' | decimal 117 118 # little-endian literals have normal prefix syntax (eg 0xffee) 119 little = decimal | '0' + (b + binary | o + octal | x + hex_) 120 121 # big-endian literals have postfix (eg ffeex0) 122 big = (binary + b | octal + o | hex_ + x) + '0' 123 124 # optional spaces - will be ignored 125 # (use DFA here because it's multi-line, so \n will match ok) 126 spaces = Drop(DfaRegexp('[ \t\n\r]*')) 127 128 with Separator(spaces): 129 130 # the grammar is recursive - expressions can contain expressions - 131 # so we use a delayed matcher here as a placeholder, so that we can 132 # use them before they are defined. 133 expr = Delayed() 134 135 # an implicit length value can be big or little-endian 136 ivalue = big | little > args(BitString.from_int) 137 138 # a value with a length can also be decimal 139 lvalue = (big | little | dec) & Drop('/') & length \ 140 > args(BitString.from_int) 141 142 value = lvalue | ivalue 143 144 repeat = value & Drop('*') & little > args(mult) 145 146 # a named value is also a tuple 147 named = name & Drop('=') & (expr | value | repeat) > tuple 148 149 # an entry in the expression could be any of these 150 entry = named | value | repeat | expr 151 152 # and an expression itself consists of a comma-separated list of 153 # one or more entries, surrounded by paremtheses 154 entries = Drop('(') & entry[1:, Drop(',')] & Drop(')') 155 156 # the Binary node may be explicit or implicit and takes the list of 157 # entries as an argument list 158 node = Optional(Drop('Node')) & entries > Node 159 160 # alternatively, we can give a name and create a named sub-class 161 other = name & entries > args(named_class) 162 163 # and finally, we "tie the knot" by giving a definition for the 164 # delayed matcher we introduced earlier, which is either a binary 165 # node or a subclass 166 expr += spaces & (node | other) & spaces 167 168 #expr = Trace(expr) 169 # this changes order, making 0800x0 parse as binary 170 expr.config.no_compile_to_regexp() 171 # use sequence to force regexp over multiple lines 172 return expr.get_parse_sequence() 173 174 175 __PARSER = None 176
177 - def parse(spec):
178 ''' 179 Use the parser. 180 ''' 181 #from logging import basicConfig, DEBUG 182 #basicConfig(level=DEBUG) 183 from lepl.stream.maxdepth import FullFirstMatchException 184 # pylint: disable-msg=W0603 185 # global 186 global __PARSER 187 if __PARSER is None: 188 __PARSER = make_binary_parser() 189 try: 190 result = __PARSER(spec) 191 except FullFirstMatchException: 192 result = None 193 if result: 194 return result[0] 195 else: 196 raise ValueError('Cannot parse: {0!r}'.format(spec))
197