Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Adding this expressions #23

Closed
wants to merge 1 commit into from

1 participant

@tomerfiliba

I backported the this expression that I introduced to construct3 (which I don't have time to finish). So I decided it would be a good idea to backport it. It's 100% backwards compatible, so there's no harm whatsoever, and it's much more readable and concise than those ugly lambdas.

In short:

  • instead of Field(..., lambda ctx: ctx.length) use Field(..., this.length)
  • Instead of Field(..., lambda ctx: ctx._.length * 4) use Field(..., this._.length * 4)
  • Instead of If(lambda ctx: ctx["foo"] > 17, ...), use If(this.foo > 17, ...)

I included a unit test (tests/test_this.py).

By the way, I would suggest moving the tests directory out of the package, e.g.

construct_repo/
    construct/
        core.py
        ...
    docs/
    tests/  <==
@tomerfiliba tomerfiliba 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`
4fe8e94
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 1, 2012
  1. @tomerfiliba

    Adding the `this` expression (backported from construct3). The `this`

    tomerfiliba authored
    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`
This page is out of date. Refresh to see the latest.
View
2  .gitignore
@@ -1 +1,3 @@
+.project
+.pydevproject
*.pyc
View
5 construct/__init__.py
@@ -33,13 +33,14 @@
from construct.core import *
from construct.adapters import *
from construct.macros import *
+from construct.lib.expr import this
from debug import Probe, Debugger
#===============================================================================
# Metadata
#===============================================================================
-__author__ = "tomer filiba (tomerfiliba [at] gmail.com)"
+__author__ = "tomer filiba <tomerfiliba@gmail.com>"
__maintainer__ = "Corbin Simpson <MostAwesomeDude@gmail.com>"
__version__ = "2.06"
@@ -106,5 +107,5 @@ def wrapper(*args, **kwargs):
'SymmetricMapping', 'Terminator', 'TerminatorError', 'Tunnel',
'TunnelAdapter', 'UBInt16', 'UBInt32', 'UBInt64', 'UBInt8', 'ULInt16',
'ULInt32', 'ULInt64', 'ULInt8', 'UNInt16', 'UNInt32', 'UNInt64', 'UNInt8',
- 'Union', 'ValidationError', 'Validator', 'Value', "Magic",
+ 'Union', 'ValidationError', 'Validator', 'Value', "Magic", "this",
]
View
142 construct/lib/expr.py
@@ -0,0 +1,142 @@
+#
+# The 'this' expression (backported from construct3).
+#
+import operator
+
+
+class ExprMixin(object):
+ __slots__ = ()
+ def __add__(self, other):
+ return BinExpr(operator.add, self, other, "+")
+ def __sub__(self, other):
+ return BinExpr(operator.sub, self, other, "-")
+ def __mul__(self, other):
+ return BinExpr(operator.mul, self, other, "*")
+ def __floordiv__(self, other):
+ return BinExpr(operator.div, self, other, "//")
+ def __truediv__(self, other):
+ return BinExpr(operator.div, self, other, "/")
+ __div__ = __floordiv__
+ def __mod__(self, other):
+ return BinExpr(operator.mod, self, other, "%")
+ def __pow__(self, other):
+ return BinExpr(operator.pow, self, other, "**")
+ def __xor__(self, other):
+ return BinExpr(operator.xor, self, other, "^")
+ def __rshift__(self, other):
+ return BinExpr(operator.rshift, self, other, ">>")
+ def __lshift__(self, other):
+ return BinExpr(operator.rshift, self, other, "<<")
+ def __and__(self, other):
+ return BinExpr(operator.and_, self, other, "and")
+ def __or__(self, other):
+ return BinExpr(operator.or_, self, other, "or")
+
+ def __radd__(self, other):
+ return BinExpr(operator.add, other, self, "+")
+ def __rsub__(self, other):
+ return BinExpr(operator.sub, other, self, "-")
+ def __rmul__(self, other):
+ return BinExpr(operator.mul, other, self, "*")
+ def __rfloordiv__(self, other):
+ return BinExpr(operator.div, other, self, "//")
+ def __rtruediv__(self, other):
+ return BinExpr(operator.div, other, self, "/")
+ __rdiv__ = __rfloordiv__
+ def __rmod__(self, other):
+ return BinExpr(operator.mod, other, self, "%")
+ def __rpow__(self, other):
+ return BinExpr(operator.pow, other, self, "**")
+ def __rxor__(self, other):
+ return BinExpr(operator.xor, other, self, "^")
+ def __rrshift__(self, other):
+ return BinExpr(operator.rshift, other, self, ">>")
+ def __rlshift__(self, other):
+ return BinExpr(operator.rshift, other, self, "<<")
+ def __rand__(self, other):
+ return BinExpr(operator.and_, other, self, "and")
+ def __ror__(self, other):
+ return BinExpr(operator.or_, other, self, "or")
+
+ def __neg__(self):
+ return UniExpr(operator.neg, self, "-")
+ def __pos__(self):
+ return UniExpr(operator.pos, self, "+")
+ def __invert__(self):
+ return UniExpr(operator.not_, self, "not")
+ __inv__ = __invert__
+
+ def __contains__(self, other):
+ return BinExpr(operator.contains, self, other, "in")
+ def __gt__(self, other):
+ return BinExpr(operator.gt, self, other, ">")
+ def __ge__(self, other):
+ return BinExpr(operator.ge, self, other, ">=")
+ def __lt__(self, other):
+ return BinExpr(operator.lt, self, other, "<")
+ def __le__(self, other):
+ return BinExpr(operator.le, self, other, "<=")
+ def __eq__(self, other):
+ return BinExpr(operator.eq, self, other, "==")
+ def __ne__(self, other):
+ return BinExpr(operator.ne, self, other, "!=")
+
+
+class UniExpr(ExprMixin):
+ __slots__ = ["op", "operand", "opname"]
+ def __init__(self, op, operand, opname):
+ self.op = op
+ self.opname = opname
+ self.operand = operand
+ def __repr__(self):
+ return "%s %r" % (self.lhs, self.opname, self.rhs)
+ def __call__(self, context):
+ operand = self.operand(context) if callable(self.operand) else self.operand
+ return self.op(operand)
+
+
+class BinExpr(ExprMixin):
+ __slots__ = ["op", "lhs", "rhs", "opname"]
+ def __init__(self, op, lhs, rhs, opname):
+ self.op = op
+ self.opname = opname
+ self.lhs = lhs
+ self.rhs = rhs
+ def __repr__(self):
+ return "(%r %s %r)" % (self.lhs, self.opname, self.rhs)
+ def __call__(self, context):
+ lhs = self.lhs(context) if callable(self.lhs) else self.lhs
+ rhs = self.rhs(context) if callable(self.rhs) else self.rhs
+ return self.op(lhs, rhs)
+
+
+class Path(ExprMixin):
+ __slots__ = ["__name", "__parent"]
+ def __init__(self, name, parent = None):
+ self.__name = name
+ self.__parent = parent
+ def __repr__(self):
+ if self.__parent is None:
+ return self.__name
+ return "%r.%s" % (self.__parent, self.__name)
+ def __call__(self, context):
+ if self.__parent is None:
+ return context
+ context2 = self.__parent(context)
+ return context2[self.__name]
+ def __getattr__(self, name):
+ return Path(name, self)
+
+
+# let the magic begin!
+this = Path("this")
+
+
+if __name__ == "__main__":
+ x = (this.foo * 2 + 3 << 2) % 11
+ print x
+ print x({"foo" : 7})
+
+
+
+
View
43 construct/tests/test_this.py
@@ -0,0 +1,43 @@
+import unittest
+from construct import Struct, UBInt8, UBInt32, IfThenElse, Value, Field, Container
+from construct import this
+
+
+class TestThisExpressions(unittest.TestCase):
+
+ this_example = Struct("this_example",
+ # straight-forward usage: instead of passing (lambda ctx: ctx["length"]) use this.length
+ UBInt8("length"),
+ Field("value", this.length),
+
+ # an example of nesting: '_' refers to the parent's scope
+ Struct("nested",
+ UBInt8("b1"),
+ UBInt8("b2"),
+ Value("b3", this.b1 * this.b2 + this._.length)
+ ),
+
+ # and conditions work as expected
+ IfThenElse("condition", this.nested.b1 > 50,
+ UBInt32("foo"),
+ UBInt8("bar"),
+ )
+ )
+
+ def test_parse(self):
+ res = self.this_example.parse("\x05helloABXXXX")
+ expected = Container(length = 5, value = 'hello',
+ nested = Container(b1 = 65, b2 = 66, b3 = 4295),
+ condition = 1482184792)
+ self.assertEquals(res, expected)
+
+ def test_build(self):
+ obj = Container(length = 5, value = 'hello',
+ nested = Container(b1 = 65, b2 = 66, b3 = None), condition = 1482184792)
+ data = self.this_example.build(obj)
+ self.assertEquals(data, "\x05helloABXXXX")
+
+
+if __name__ == "__main__":
+ unittest.main()
+
View
2  setup.py
@@ -11,7 +11,7 @@
long_description=open("README.rst").read(),
url="https://github.com/MostAwesomeDude/construct",
author="Tomer Filiba",
- author_email="tomerfiliba at gmail dot com",
+ author_email="tomerfiliba@gmail.com",
maintainer="Corbin Simpson",
maintainer_email="MostAwesomeDude@gmail.com",
)
Something went wrong with that request. Please try again.