Skip to content

Commit 969e568

Browse files
Tomer Filibatomerfiliba
Tomer Filiba
authored andcommitted
Adding the this expression (backported from construct3). The this
expression builds an evaluatable (callable) object directly from python expressions, so it's less verbose and much easier to read and write. Also, it's compatible with the existing lambda functions. Instead of doing `lambda ctx: ctx["length"] * 2`, simply use `this.length * 2`
1 parent 1478482 commit 969e568

File tree

5 files changed

+195
-7
lines changed

5 files changed

+195
-7
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
.project
2+
.pydevproject
13
*.pyc

construct/__init__.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,17 @@
3030
"\\x01\\x02\\x03"
3131
"""
3232

33-
from .core import *
34-
from .adapters import *
35-
from .macros import *
36-
from .debug import Probe, Debugger
33+
from construct.core import *
34+
from construct.adapters import *
35+
from construct.macros import *
36+
from construct.lib.expr import this
37+
from construct.debug import Probe, Debugger
3738

3839

3940
#===============================================================================
4041
# Metadata
4142
#===============================================================================
42-
__author__ = "tomer filiba (tomerfiliba [at] gmail.com)"
43+
__author__ = "tomer filiba <tomerfiliba@gmail.com>"
4344
__maintainer__ = "Corbin Simpson <MostAwesomeDude@gmail.com>"
4445
__version__ = "2.06"
4546

@@ -106,5 +107,5 @@ def wrapper(*args, **kwargs):
106107
'SymmetricMapping', 'Terminator', 'TerminatorError', 'Tunnel',
107108
'TunnelAdapter', 'UBInt16', 'UBInt32', 'UBInt64', 'UBInt8', 'ULInt16',
108109
'ULInt32', 'ULInt64', 'ULInt8', 'UNInt16', 'UNInt32', 'UNInt64', 'UNInt8',
109-
'Union', 'ValidationError', 'Validator', 'Value', "Magic",
110+
'Union', 'ValidationError', 'Validator', 'Value', "Magic", "this",
110111
]

construct/lib/expr.py

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#
2+
# The 'this' expression (backported from construct3).
3+
#
4+
import operator
5+
6+
7+
class ExprMixin(object):
8+
__slots__ = ()
9+
def __add__(self, other):
10+
return BinExpr(operator.add, self, other, "+")
11+
def __sub__(self, other):
12+
return BinExpr(operator.sub, self, other, "-")
13+
def __mul__(self, other):
14+
return BinExpr(operator.mul, self, other, "*")
15+
def __floordiv__(self, other):
16+
return BinExpr(operator.div, self, other, "//")
17+
def __truediv__(self, other):
18+
return BinExpr(operator.div, self, other, "/")
19+
__div__ = __floordiv__
20+
def __mod__(self, other):
21+
return BinExpr(operator.mod, self, other, "%")
22+
def __pow__(self, other):
23+
return BinExpr(operator.pow, self, other, "**")
24+
def __xor__(self, other):
25+
return BinExpr(operator.xor, self, other, "^")
26+
def __rshift__(self, other):
27+
return BinExpr(operator.rshift, self, other, ">>")
28+
def __lshift__(self, other):
29+
return BinExpr(operator.rshift, self, other, "<<")
30+
def __and__(self, other):
31+
return BinExpr(operator.and_, self, other, "and")
32+
def __or__(self, other):
33+
return BinExpr(operator.or_, self, other, "or")
34+
35+
def __radd__(self, other):
36+
return BinExpr(operator.add, other, self, "+")
37+
def __rsub__(self, other):
38+
return BinExpr(operator.sub, other, self, "-")
39+
def __rmul__(self, other):
40+
return BinExpr(operator.mul, other, self, "*")
41+
def __rfloordiv__(self, other):
42+
return BinExpr(operator.div, other, self, "//")
43+
def __rtruediv__(self, other):
44+
return BinExpr(operator.div, other, self, "/")
45+
__rdiv__ = __rfloordiv__
46+
def __rmod__(self, other):
47+
return BinExpr(operator.mod, other, self, "%")
48+
def __rpow__(self, other):
49+
return BinExpr(operator.pow, other, self, "**")
50+
def __rxor__(self, other):
51+
return BinExpr(operator.xor, other, self, "^")
52+
def __rrshift__(self, other):
53+
return BinExpr(operator.rshift, other, self, ">>")
54+
def __rlshift__(self, other):
55+
return BinExpr(operator.rshift, other, self, "<<")
56+
def __rand__(self, other):
57+
return BinExpr(operator.and_, other, self, "and")
58+
def __ror__(self, other):
59+
return BinExpr(operator.or_, other, self, "or")
60+
61+
def __neg__(self):
62+
return UniExpr(operator.neg, self, "-")
63+
def __pos__(self):
64+
return UniExpr(operator.pos, self, "+")
65+
def __invert__(self):
66+
return UniExpr(operator.not_, self, "not")
67+
__inv__ = __invert__
68+
69+
def __contains__(self, other):
70+
return BinExpr(operator.contains, self, other, "in")
71+
def __gt__(self, other):
72+
return BinExpr(operator.gt, self, other, ">")
73+
def __ge__(self, other):
74+
return BinExpr(operator.ge, self, other, ">=")
75+
def __lt__(self, other):
76+
return BinExpr(operator.lt, self, other, "<")
77+
def __le__(self, other):
78+
return BinExpr(operator.le, self, other, "<=")
79+
def __eq__(self, other):
80+
return BinExpr(operator.eq, self, other, "==")
81+
def __ne__(self, other):
82+
return BinExpr(operator.ne, self, other, "!=")
83+
84+
85+
class UniExpr(ExprMixin):
86+
__slots__ = ["op", "operand", "opname"]
87+
def __init__(self, op, operand, opname):
88+
self.op = op
89+
self.opname = opname
90+
self.operand = operand
91+
def __repr__(self):
92+
return "%s %r" % (self.lhs, self.opname, self.rhs)
93+
def __call__(self, context):
94+
operand = self.operand(context) if callable(self.operand) else self.operand
95+
return self.op(operand)
96+
97+
98+
class BinExpr(ExprMixin):
99+
__slots__ = ["op", "lhs", "rhs", "opname"]
100+
def __init__(self, op, lhs, rhs, opname):
101+
self.op = op
102+
self.opname = opname
103+
self.lhs = lhs
104+
self.rhs = rhs
105+
def __repr__(self):
106+
return "(%r %s %r)" % (self.lhs, self.opname, self.rhs)
107+
def __call__(self, context):
108+
lhs = self.lhs(context) if callable(self.lhs) else self.lhs
109+
rhs = self.rhs(context) if callable(self.rhs) else self.rhs
110+
return self.op(lhs, rhs)
111+
112+
113+
class Path(ExprMixin):
114+
__slots__ = ["__name", "__parent"]
115+
def __init__(self, name, parent = None):
116+
self.__name = name
117+
self.__parent = parent
118+
def __repr__(self):
119+
if self.__parent is None:
120+
return self.__name
121+
return "%r.%s" % (self.__parent, self.__name)
122+
def __call__(self, context):
123+
if self.__parent is None:
124+
return context
125+
context2 = self.__parent(context)
126+
return context2[self.__name]
127+
def __getattr__(self, name):
128+
return Path(name, self)
129+
130+
131+
# let the magic begin!
132+
this = Path("this")
133+
134+
135+
if __name__ == "__main__":
136+
x = (this.foo * 2 + 3 << 2) % 11
137+
print x
138+
print x({"foo" : 7})
139+
140+
141+
142+

construct/tests/test_this.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import unittest
2+
from construct import Struct, UBInt8, UBInt32, IfThenElse, Value, Field, Container
3+
from construct import this
4+
5+
6+
class TestThisExpressions(unittest.TestCase):
7+
8+
this_example = Struct("this_example",
9+
# straight-forward usage: instead of passing (lambda ctx: ctx["length"]) use this.length
10+
UBInt8("length"),
11+
Field("value", this.length),
12+
13+
# an example of nesting: '_' refers to the parent's scope
14+
Struct("nested",
15+
UBInt8("b1"),
16+
UBInt8("b2"),
17+
Value("b3", this.b1 * this.b2 + this._.length)
18+
),
19+
20+
# and conditions work as expected
21+
IfThenElse("condition", this.nested.b1 > 50,
22+
UBInt32("foo"),
23+
UBInt8("bar"),
24+
)
25+
)
26+
27+
def test_parse(self):
28+
res = self.this_example.parse("\x05helloABXXXX")
29+
expected = Container(length = 5, value = 'hello',
30+
nested = Container(b1 = 65, b2 = 66, b3 = 4295),
31+
condition = 1482184792)
32+
self.assertEquals(res, expected)
33+
34+
def test_build(self):
35+
obj = Container(length = 5, value = 'hello',
36+
nested = Container(b1 = 65, b2 = 66, b3 = None), condition = 1482184792)
37+
data = self.this_example.build(obj)
38+
self.assertEquals(data, "\x05helloABXXXX")
39+
40+
41+
if __name__ == "__main__":
42+
unittest.main()
43+

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
long_description=open("README.rst").read(),
1212
url="https://github.com/MostAwesomeDude/construct",
1313
author="Tomer Filiba",
14-
author_email="tomerfiliba at gmail dot com",
14+
author_email="tomerfiliba@gmail.com",
1515
maintainer="Corbin Simpson",
1616
maintainer_email="MostAwesomeDude@gmail.com",
1717
)

0 commit comments

Comments
 (0)