| Home | Trees | Indices | Help |
|---|
|
|
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
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
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
| Home | Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Sat Jun 9 21:51:02 2012 | http://epydoc.sourceforge.net |