scripts: csv.py: Filled out remaining ops, dropped bitwise ops, cleanup
So csv.py should now be mostly feature complete, aside from bugs. I ended up dropping most of the bitwise operations for now. I can't really see them being useful since csv.py and related scripts are usually operating on purely numerical data. Worst case we can always add them back in at some point. I also considered dropping the logical/ternary operators, but even though I don't see an immediate use case, the flexibility logical/ternary operators add to a language is too much to pass on. Another interesting thing to note is the extension of all fold functions to operate on exprs if more than one argument is provided: - max(1) => 1, fold=max - max(1, 2) => 2, fold=sum - max(1, 2, 3) => 3, fold=sum To be honest, this is mainly just to allow a binary max/min function without awkward naming conflicts. Other than those changes this was pretty simple fill-out-the-definition work.
This commit is contained in:
+343
-161
@@ -44,7 +44,8 @@ class RInt(co.namedtuple('RInt', 'x')):
|
|||||||
x = -mt.inf
|
x = -mt.inf
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
assert isinstance(x, int) or mt.isinf(x), x
|
if not (isinstance(x, int) or mt.isinf(x)):
|
||||||
|
x = int(x)
|
||||||
return super().__new__(cls, x)
|
return super().__new__(cls, x)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -55,6 +56,9 @@ class RInt(co.namedtuple('RInt', 'x')):
|
|||||||
else:
|
else:
|
||||||
return str(self.x)
|
return str(self.x)
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(self.x)
|
||||||
|
|
||||||
def __int__(self):
|
def __int__(self):
|
||||||
assert not mt.isinf(self.x)
|
assert not mt.isinf(self.x)
|
||||||
return self.x
|
return self.x
|
||||||
@@ -93,6 +97,15 @@ class RInt(co.namedtuple('RInt', 'x')):
|
|||||||
else:
|
else:
|
||||||
return (new-old) / old
|
return (new-old) / old
|
||||||
|
|
||||||
|
def __pos__(self):
|
||||||
|
return self.__class__(+self.x)
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
return self.__class__(-self.x)
|
||||||
|
|
||||||
|
def __abs__(self):
|
||||||
|
return self.__class__(abs(self.x))
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return self.__class__(self.x + other.x)
|
return self.__class__(self.x + other.x)
|
||||||
|
|
||||||
@@ -102,6 +115,12 @@ class RInt(co.namedtuple('RInt', 'x')):
|
|||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
return self.__class__(self.x * other.x)
|
return self.__class__(self.x * other.x)
|
||||||
|
|
||||||
|
def __div__(self, other):
|
||||||
|
return self.__class__(self.x // other.x)
|
||||||
|
|
||||||
|
def __mod__(self, other):
|
||||||
|
return self.__class__(self.x % other.x)
|
||||||
|
|
||||||
# float fields
|
# float fields
|
||||||
class RFloat(co.namedtuple('RFloat', 'x')):
|
class RFloat(co.namedtuple('RFloat', 'x')):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
@@ -119,7 +138,8 @@ class RFloat(co.namedtuple('RFloat', 'x')):
|
|||||||
x = -mt.inf
|
x = -mt.inf
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
assert isinstance(x, float), x
|
if not isinstance(x, float):
|
||||||
|
x = float(x)
|
||||||
return super().__new__(cls, x)
|
return super().__new__(cls, x)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -130,6 +150,12 @@ class RFloat(co.namedtuple('RFloat', 'x')):
|
|||||||
else:
|
else:
|
||||||
return '%.1f' % self.x
|
return '%.1f' % self.x
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(self.x)
|
||||||
|
|
||||||
|
def __int__(self):
|
||||||
|
return int(self.x)
|
||||||
|
|
||||||
def __float__(self):
|
def __float__(self):
|
||||||
return float(self.x)
|
return float(self.x)
|
||||||
|
|
||||||
@@ -148,10 +174,16 @@ class RFloat(co.namedtuple('RFloat', 'x')):
|
|||||||
return '%+7.1f' % diff
|
return '%+7.1f' % diff
|
||||||
|
|
||||||
ratio = RInt.ratio
|
ratio = RInt.ratio
|
||||||
|
__pos__ = RInt.__pos__
|
||||||
|
__neg__ = RInt.__neg__
|
||||||
|
__abs__ = RInt.__abs__
|
||||||
__add__ = RInt.__add__
|
__add__ = RInt.__add__
|
||||||
__sub__ = RInt.__sub__
|
__sub__ = RInt.__sub__
|
||||||
__mul__ = RInt.__mul__
|
__mul__ = RInt.__mul__
|
||||||
|
|
||||||
|
def __div__(self, other):
|
||||||
|
return self.__class__(self.x / other.x)
|
||||||
|
|
||||||
# fractional fields, a/b
|
# fractional fields, a/b
|
||||||
class RFrac(co.namedtuple('RFrac', 'a,b')):
|
class RFrac(co.namedtuple('RFrac', 'a,b')):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
@@ -167,6 +199,12 @@ class RFrac(co.namedtuple('RFrac', 'a,b')):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s/%s' % (self.a, self.b)
|
return '%s/%s' % (self.a, self.b)
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(self.a)
|
||||||
|
|
||||||
|
def __int__(self):
|
||||||
|
return int(self.a)
|
||||||
|
|
||||||
def __float__(self):
|
def __float__(self):
|
||||||
return float(self.a)
|
return float(self.a)
|
||||||
|
|
||||||
@@ -194,6 +232,15 @@ class RFrac(co.namedtuple('RFrac', 'a,b')):
|
|||||||
old = old_a.x/old_b.x if old_b.x else 1.0
|
old = old_a.x/old_b.x if old_b.x else 1.0
|
||||||
return new - old
|
return new - old
|
||||||
|
|
||||||
|
def __pos__(self):
|
||||||
|
return self.__class__(+self.a, +self.b)
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
return self.__class__(-self.a, -self.b)
|
||||||
|
|
||||||
|
def __abs__(self):
|
||||||
|
return self.__class__(abs(self.a), abs(self.b))
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return self.__class__(self.a + other.a, self.b + other.b)
|
return self.__class__(self.a + other.a, self.b + other.b)
|
||||||
|
|
||||||
@@ -201,7 +248,13 @@ class RFrac(co.namedtuple('RFrac', 'a,b')):
|
|||||||
return self.__class__(self.a - other.a, self.b - other.b)
|
return self.__class__(self.a - other.a, self.b - other.b)
|
||||||
|
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
return self.__class__(self.a * other.a, self.b + other.b)
|
return self.__class__(self.a * other.a, self.b * other.b)
|
||||||
|
|
||||||
|
def __div__(self, other):
|
||||||
|
return self.__class__(self.a // other.a, self.b // other.b)
|
||||||
|
|
||||||
|
def __mod__(self, other):
|
||||||
|
return self.__class__(self.a % other.a, self.b % other.b)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
self_a, self_b = self if self.b.x else (RInt(1), RInt(1))
|
self_a, self_b = self if self.b.x else (RInt(1), RInt(1))
|
||||||
@@ -269,8 +322,7 @@ class RGStddev:
|
|||||||
class RExpr:
|
class RExpr:
|
||||||
# expr parsing/typechecking/etc errors
|
# expr parsing/typechecking/etc errors
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
def __init__(self, reason):
|
pass
|
||||||
self.reason = reason
|
|
||||||
|
|
||||||
# expr node base class
|
# expr node base class
|
||||||
class Expr:
|
class Expr:
|
||||||
@@ -278,21 +330,22 @@ class RExpr:
|
|||||||
for k, v in zip('abcdefghijklmnopqrstuvwxyz', args):
|
for k, v in zip('abcdefghijklmnopqrstuvwxyz', args):
|
||||||
setattr(self, k, v)
|
setattr(self, k, v)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return (getattr(self, k)
|
||||||
|
for k in it.takewhile(
|
||||||
|
lambda k: hasattr(self, k),
|
||||||
|
'abcdefghijklmnopqrstuvwxyz'))
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return sum(1 for _ in self)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s(%s)' % (
|
return '%s(%s)' % (
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
','.join(
|
','.join(repr(v) for v in self))
|
||||||
repr(getattr(self, k))
|
|
||||||
for k in it.takewhile(
|
|
||||||
lambda k: hasattr(self, k),
|
|
||||||
'abcdefghijklmnopqrstuvwxyz')))
|
|
||||||
|
|
||||||
def fields(self):
|
def fields(self):
|
||||||
return set(it.chain.from_iterable(
|
return set(it.chain.from_iterable(v.fields() for v in self))
|
||||||
getattr(self, k).fields()
|
|
||||||
for k in it.takewhile(
|
|
||||||
lambda k: hasattr(self, k),
|
|
||||||
'abcdefghijklmnopqrstuvwxyz')))
|
|
||||||
|
|
||||||
def type(self, types={}):
|
def type(self, types={}):
|
||||||
return self.a.type(types)
|
return self.a.type(types)
|
||||||
@@ -305,26 +358,6 @@ class RExpr:
|
|||||||
|
|
||||||
# expr nodes
|
# expr nodes
|
||||||
|
|
||||||
# field expr
|
|
||||||
class Field(Expr):
|
|
||||||
def fields(self):
|
|
||||||
return {self.a}
|
|
||||||
|
|
||||||
def type(self, types={}):
|
|
||||||
if self.a not in types:
|
|
||||||
raise RExpr.Error("untyped field? %s" % self.a)
|
|
||||||
return types[self.a]
|
|
||||||
|
|
||||||
def fold(self, types={}):
|
|
||||||
if self.a not in types:
|
|
||||||
raise RExpr.Error("unfoldable field? %s" % self.a)
|
|
||||||
return RSum, types[self.a]
|
|
||||||
|
|
||||||
def eval(self, fields={}):
|
|
||||||
if self.a not in fields:
|
|
||||||
raise RExpr.Error("unknown field? %s" % self.a)
|
|
||||||
return fields[self.a]
|
|
||||||
|
|
||||||
# literal exprs
|
# literal exprs
|
||||||
class StrLit(Expr):
|
class StrLit(Expr):
|
||||||
def fields(self):
|
def fields(self):
|
||||||
@@ -359,6 +392,26 @@ class RExpr:
|
|||||||
def eval(self, fields={}):
|
def eval(self, fields={}):
|
||||||
return self.a
|
return self.a
|
||||||
|
|
||||||
|
# field expr
|
||||||
|
class Field(Expr):
|
||||||
|
def fields(self):
|
||||||
|
return {self.a}
|
||||||
|
|
||||||
|
def type(self, types={}):
|
||||||
|
if self.a not in types:
|
||||||
|
raise RExpr.Error("untyped field? %s" % self.a)
|
||||||
|
return types[self.a]
|
||||||
|
|
||||||
|
def fold(self, types={}):
|
||||||
|
if self.a not in types:
|
||||||
|
raise RExpr.Error("unfoldable field? %s" % self.a)
|
||||||
|
return RSum, types[self.a]
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if self.a not in fields:
|
||||||
|
raise RExpr.Error("unknown field? %s" % self.a)
|
||||||
|
return fields[self.a]
|
||||||
|
|
||||||
# func expr helper
|
# func expr helper
|
||||||
def func(name):
|
def func(name):
|
||||||
def func(f):
|
def func(f):
|
||||||
@@ -403,71 +456,213 @@ class RExpr:
|
|||||||
@func('sum')
|
@func('sum')
|
||||||
class Sum(Expr):
|
class Sum(Expr):
|
||||||
def fold(self, types={}):
|
def fold(self, types={}):
|
||||||
return RSum, self.a.type(types)
|
if len(self) == 1:
|
||||||
|
return RSum, self.a.type(types)
|
||||||
|
else:
|
||||||
|
return self.a.fold(types)
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.eval(fields)
|
||||||
|
else:
|
||||||
|
return RSum()([v.eval(fields) for v in self])
|
||||||
|
|
||||||
@func('prod')
|
@func('prod')
|
||||||
class Prod(Expr):
|
class Prod(Expr):
|
||||||
def fold(self, types={}):
|
def fold(self, types={}):
|
||||||
return RProd, self.a.type(types)
|
if len(self) == 1:
|
||||||
|
return Prod, self.a.type(types)
|
||||||
|
else:
|
||||||
|
return self.a.fold(types)
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.eval(fields)
|
||||||
|
else:
|
||||||
|
return Prod()([v.eval(fields) for v in self])
|
||||||
|
|
||||||
@func('min')
|
@func('min')
|
||||||
class Min(Expr):
|
class Min(Expr):
|
||||||
def fold(self, types={}):
|
def fold(self, types={}):
|
||||||
return RMin, self.a.type(types)
|
if len(self) == 1:
|
||||||
|
return RMin, self.a.type(types)
|
||||||
|
else:
|
||||||
|
return self.a.fold(types)
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.eval(fields)
|
||||||
|
else:
|
||||||
|
return RMin()([v.eval(fields) for v in self])
|
||||||
|
|
||||||
@func('max')
|
@func('max')
|
||||||
class Max(Expr):
|
class Max(Expr):
|
||||||
def fold(self, types={}):
|
def fold(self, types={}):
|
||||||
return RMax, self.a.type(types)
|
if len(self) == 1:
|
||||||
|
return RMax, self.a.type(types)
|
||||||
|
else:
|
||||||
|
return self.a.fold(types)
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.eval(fields)
|
||||||
|
else:
|
||||||
|
return RMax()([v.eval(fields) for v in self])
|
||||||
|
|
||||||
@func('avg')
|
@func('avg')
|
||||||
class Avg(Expr):
|
class Avg(Expr):
|
||||||
|
def type(self, types={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.type(types)
|
||||||
|
else:
|
||||||
|
return RFloat
|
||||||
|
|
||||||
def fold(self, types={}):
|
def fold(self, types={}):
|
||||||
return RAvg, RFloat
|
if len(self) == 1:
|
||||||
|
return RAvg, RFloat
|
||||||
|
else:
|
||||||
|
return self.a.fold(types)
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.eval(fields)
|
||||||
|
else:
|
||||||
|
return RAvg()([v.eval(fields) for v in self])
|
||||||
|
|
||||||
@func('stddev')
|
@func('stddev')
|
||||||
class Stddev(Expr):
|
class Stddev(Expr):
|
||||||
|
def type(self, types={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.type(types)
|
||||||
|
else:
|
||||||
|
return RFloat
|
||||||
|
|
||||||
def fold(self, types={}):
|
def fold(self, types={}):
|
||||||
return RStddev, RFloat
|
if len(self) == 1:
|
||||||
|
return RStddev, RFloat
|
||||||
|
else:
|
||||||
|
return self.a.fold(types)
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.eval(fields)
|
||||||
|
else:
|
||||||
|
return RStddev()([v.eval(fields) for v in self])
|
||||||
|
|
||||||
@func('gmean')
|
@func('gmean')
|
||||||
class GMean(Expr):
|
class GMean(Expr):
|
||||||
|
def type(self, types={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.type(types)
|
||||||
|
else:
|
||||||
|
return RFloat
|
||||||
|
|
||||||
def fold(self, types={}):
|
def fold(self, types={}):
|
||||||
return RGMean, RFloat
|
if len(self) == 1:
|
||||||
|
return RGMean, RFloat
|
||||||
|
else:
|
||||||
|
return self.a.fold(types)
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.eval(fields)
|
||||||
|
else:
|
||||||
|
return RGMean()([v.eval(fields) for v in self])
|
||||||
|
|
||||||
@func('stddev')
|
@func('stddev')
|
||||||
class GStddev(Expr):
|
class GStddev(Expr):
|
||||||
|
def type(self, types={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.type(types)
|
||||||
|
else:
|
||||||
|
return RFloat
|
||||||
|
|
||||||
def fold(self, types={}):
|
def fold(self, types={}):
|
||||||
return RGStddev, RFloat
|
if len(self) == 1:
|
||||||
|
return RGStddev, RFloat
|
||||||
|
else:
|
||||||
|
return self.a.fold(types)
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self.a.eval(fields)
|
||||||
|
else:
|
||||||
|
return RGStddev()([v.eval(fields) for v in self])
|
||||||
|
|
||||||
# functions
|
# functions
|
||||||
@func('ratio')
|
@func('ratio')
|
||||||
class Ratio(Expr):
|
class Ratio(Expr):
|
||||||
pass
|
def type(self, types={}):
|
||||||
|
return RFloat
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
v = self.a.eval(fields)
|
||||||
|
return RFloat(float(v.a) / float(v.b))
|
||||||
|
|
||||||
@func('total')
|
@func('total')
|
||||||
class Total(Expr):
|
class Total(Expr):
|
||||||
pass
|
def type(self, types={}):
|
||||||
|
return RInt
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
return self.a.eval(fields).b
|
||||||
|
|
||||||
|
@func('abs')
|
||||||
|
class Abs(Expr):
|
||||||
|
def eval(self, fields={}):
|
||||||
|
return abs(self.a.eval(fields))
|
||||||
|
|
||||||
@func('ceil')
|
@func('ceil')
|
||||||
class Ceil(Expr):
|
class Ceil(Expr):
|
||||||
pass
|
def type(self, types={}):
|
||||||
|
return RFloat
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
return RFloat(mt.ceil(float(self.a.eval(fields))))
|
||||||
|
|
||||||
@func('floor')
|
@func('floor')
|
||||||
class Floor(Expr):
|
class Floor(Expr):
|
||||||
pass
|
def type(self, types={}):
|
||||||
|
return RFloat
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
return RFloat(mt.floor(float(self.a.eval(fields))))
|
||||||
|
|
||||||
@func('log')
|
@func('log')
|
||||||
class Log(Expr):
|
class Log(Expr):
|
||||||
pass
|
def type(self, types={}):
|
||||||
|
return RFloat
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return RFloat(mt.log(
|
||||||
|
float(self.a.eval(fields))))
|
||||||
|
else:
|
||||||
|
return RFloat(mt.log(
|
||||||
|
float(self.a.eval(fields)),
|
||||||
|
float(self.b.eval(fields))))
|
||||||
|
|
||||||
@func('pow')
|
@func('pow')
|
||||||
class Pow(Expr):
|
class Pow(Expr):
|
||||||
pass
|
def type(self, types={}):
|
||||||
|
return RFloat
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if len(self) == 1:
|
||||||
|
return RFloat(mt.exp(
|
||||||
|
float(self.a.eval(fields))))
|
||||||
|
else:
|
||||||
|
return RFloat(mt.pow(
|
||||||
|
float(self.a.eval(fields)),
|
||||||
|
float(self.b.eval(fields))))
|
||||||
|
|
||||||
@func('sqrt')
|
@func('sqrt')
|
||||||
class Sqrt(Expr):
|
class Sqrt(Expr):
|
||||||
pass
|
def type(self, types={}):
|
||||||
|
return RFloat
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
return RFloat(mt.sqrt(float(self.a.eval(fields))))
|
||||||
|
|
||||||
# unary expr helper
|
# unary expr helper
|
||||||
def uop(op):
|
def uop(op):
|
||||||
@@ -487,19 +682,24 @@ class RExpr:
|
|||||||
# unary ops
|
# unary ops
|
||||||
@uop('+')
|
@uop('+')
|
||||||
class Pos(Expr):
|
class Pos(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
return +self.a.eval(fields)
|
||||||
|
|
||||||
@uop('-')
|
@uop('-')
|
||||||
class Neg(Expr):
|
class Neg(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
return -self.a.eval(fields)
|
||||||
@uop('~')
|
|
||||||
class Not(Expr):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@uop('!')
|
@uop('!')
|
||||||
class Notnot(Expr):
|
class Not(Expr):
|
||||||
pass
|
def type(self, types={}):
|
||||||
|
return RInt
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
if self.a.eval(fields):
|
||||||
|
return RInt(0)
|
||||||
|
else:
|
||||||
|
return RInt(1)
|
||||||
|
|
||||||
# binary expr help
|
# binary expr help
|
||||||
def bop(op, prec):
|
def bop(op, prec):
|
||||||
@@ -528,75 +728,94 @@ class RExpr:
|
|||||||
# binary ops
|
# binary ops
|
||||||
@bop('*', 10)
|
@bop('*', 10)
|
||||||
class Mul(Expr):
|
class Mul(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
return self.a.eval(fields) * self.b.eval(fields)
|
||||||
|
|
||||||
@bop('/', 10)
|
@bop('/', 10)
|
||||||
class Div(Expr):
|
class Div(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
return self.a.eval(fields) / self.b.eval(fields)
|
||||||
|
|
||||||
@bop('%', 10)
|
@bop('%', 10)
|
||||||
class Mod(Expr):
|
class Mod(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
return self.a.eval(fields) % self.b.eval(fields)
|
||||||
|
|
||||||
@bop('+', 9)
|
@bop('+', 9)
|
||||||
class Add(Expr):
|
class Add(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
return self.a.eval(fields) + self.b.eval(fields)
|
||||||
|
|
||||||
@bop('-', 9)
|
@bop('-', 9)
|
||||||
class Sub(Expr):
|
class Sub(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
return self.a.eval(fields) - self.b.eval(fields)
|
||||||
@bop('<<', 8)
|
|
||||||
class Shl(Expr):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@bop('>>', 8)
|
|
||||||
class Shr(Expr):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@bop('&', 7)
|
|
||||||
class And(Expr):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@bop('^', 6)
|
|
||||||
class Xor(Expr):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@bop('|', 5)
|
|
||||||
class Or(Expr):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@bop('==', 4)
|
@bop('==', 4)
|
||||||
class Eq(Expr):
|
class Eq(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
if self.a.eval(fields) == self.b.eval(fields):
|
||||||
|
return RInt(1)
|
||||||
|
else:
|
||||||
|
return RInt(0)
|
||||||
|
|
||||||
@bop('!=', 4)
|
@bop('!=', 4)
|
||||||
class Ne(Expr):
|
class Ne(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
if self.a.eval(fields) != self.b.eval(fields):
|
||||||
|
return RInt(1)
|
||||||
|
else:
|
||||||
|
return RInt(0)
|
||||||
|
|
||||||
@bop('<', 4)
|
@bop('<', 4)
|
||||||
class Lt(Expr):
|
class Lt(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
if self.a.eval(fields) < self.b.eval(fields):
|
||||||
|
return RInt(1)
|
||||||
|
else:
|
||||||
|
return RInt(0)
|
||||||
|
|
||||||
@bop('<=', 4)
|
@bop('<=', 4)
|
||||||
class Le(Expr):
|
class Le(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
if self.a.eval(fields) <= self.b.eval(fields):
|
||||||
|
return RInt(1)
|
||||||
|
else:
|
||||||
|
return RInt(0)
|
||||||
|
|
||||||
@bop('>', 4)
|
@bop('>', 4)
|
||||||
class Gt(Expr):
|
class Gt(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
if self.a.eval(fields) > self.b.eval(fields):
|
||||||
|
return RInt(1)
|
||||||
|
else:
|
||||||
|
return RInt(0)
|
||||||
|
|
||||||
@bop('>=', 4)
|
@bop('>=', 4)
|
||||||
class Ge(Expr):
|
class Ge(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
if self.a.eval(fields) >= self.b.eval(fields):
|
||||||
|
return RInt(1)
|
||||||
|
else:
|
||||||
|
return RInt(0)
|
||||||
|
|
||||||
@bop('&&', 3)
|
@bop('&&', 3)
|
||||||
class Andand(Expr):
|
class And(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
a = self.a.eval(fields)
|
||||||
|
if a:
|
||||||
|
return self.b.eval(fields)
|
||||||
|
else:
|
||||||
|
return a
|
||||||
|
|
||||||
@bop('||', 2)
|
@bop('||', 2)
|
||||||
class Oror(Expr):
|
class Oror(Expr):
|
||||||
pass
|
def eval(self, fields={}):
|
||||||
|
a = self.a.eval(fields)
|
||||||
|
if a:
|
||||||
|
return a
|
||||||
|
else:
|
||||||
|
return self.b.eval(fields)
|
||||||
|
|
||||||
# ternary ops
|
# ternary ops
|
||||||
class Ife(Expr):
|
class Ife(Expr):
|
||||||
@@ -606,6 +825,13 @@ class RExpr:
|
|||||||
def fold(self, types={}):
|
def fold(self, types={}):
|
||||||
return self.b.fold(types)
|
return self.b.fold(types)
|
||||||
|
|
||||||
|
def eval(self, fields={}):
|
||||||
|
a = self.a.eval(fields)
|
||||||
|
if a:
|
||||||
|
return self.b.eval(fields)
|
||||||
|
else:
|
||||||
|
return self.c.eval(fields)
|
||||||
|
|
||||||
# parse an expr
|
# parse an expr
|
||||||
def __init__(self, expr):
|
def __init__(self, expr):
|
||||||
self.expr = expr.strip()
|
self.expr = expr.strip()
|
||||||
@@ -619,6 +845,24 @@ class RExpr:
|
|||||||
raise RExpr.Error("mismatched parens? %s" % tail)
|
raise RExpr.Error("mismatched parens? %s" % tail)
|
||||||
tail = tail[1:].lstrip()
|
tail = tail[1:].lstrip()
|
||||||
|
|
||||||
|
# strings
|
||||||
|
elif re.match('(?:"(?:\\.|[^"])*"|\'(?:\\.|[^\'])\')', expr):
|
||||||
|
m = re.match('(?:"(?:\\.|[^"])*"|\'(?:\\.|[^\'])\')', expr)
|
||||||
|
a = RExpr.StrLit(m.group()[1:-1])
|
||||||
|
tail = expr[len(m.group()):].lstrip()
|
||||||
|
|
||||||
|
# floats
|
||||||
|
elif re.match('[+-]?[_0-9]*\.[_0-9eE]', expr):
|
||||||
|
m = re.match('[+-]?[_0-9]*\.[_0-9eE]', expr)
|
||||||
|
a = RExpr.FloatLit(RFloat(m.group()))
|
||||||
|
tail = expr[len(m.group()):].lstrip()
|
||||||
|
|
||||||
|
# ints
|
||||||
|
elif re.match('[+-]?(?:[0-9][bBoOxX]?[_0-9a-fA-F]*|∞|inf)', expr):
|
||||||
|
m = re.match('[+-]?(?:[0-9][bBoOxX]?[_0-9a-fA-F]*|∞|inf)', expr)
|
||||||
|
a = RExpr.IntLit(RInt(m.group()))
|
||||||
|
tail = expr[len(m.group()):].lstrip()
|
||||||
|
|
||||||
# fields/functions
|
# fields/functions
|
||||||
elif re.match('[_a-zA-Z][_a-zA-Z0-9]*', expr):
|
elif re.match('[_a-zA-Z][_a-zA-Z0-9]*', expr):
|
||||||
m = re.match('[_a-zA-Z][_a-zA-Z0-9]*', expr)
|
m = re.match('[_a-zA-Z][_a-zA-Z0-9]*', expr)
|
||||||
@@ -646,30 +890,12 @@ class RExpr:
|
|||||||
else:
|
else:
|
||||||
a = RExpr.Field(m.group())
|
a = RExpr.Field(m.group())
|
||||||
|
|
||||||
# strings
|
|
||||||
elif re.match('(?:"(?:\\.|[^"])*"|\'(?:\\.|[^\'])\')', expr):
|
|
||||||
m = re.match('(?:"(?:\\.|[^"])*"|\'(?:\\.|[^\'])\')', expr)
|
|
||||||
a = RExpr.StrLit(m.group()[1:-1])
|
|
||||||
tail = expr[len(m.group()):].lstrip()
|
|
||||||
|
|
||||||
# floats
|
|
||||||
elif re.match('[+-]?[_0-9]*\.[_0-9eE]', expr):
|
|
||||||
m = re.match('[+-]?[_0-9]*\.[_0-9eE]', expr)
|
|
||||||
a = RExpr.FloatLit(RFloat(m.group()))
|
|
||||||
tail = expr[len(m.group()):].lstrip()
|
|
||||||
|
|
||||||
# ints
|
|
||||||
elif re.match('[+-]?(?:(?:0[bBoOxX])?[_0-9a-fA-F]+|∞|inf)', expr):
|
|
||||||
m = re.match('[+-]?(?:(?:0[bBoOxX])?[_0-9a-fA-F]+|∞|inf)', expr)
|
|
||||||
a = RExpr.IntLit(RInt(m.group()))
|
|
||||||
tail = expr[len(m.group()):].lstrip()
|
|
||||||
|
|
||||||
# unary ops
|
# unary ops
|
||||||
elif any(expr.startswith(op) for op in RExpr.uops.keys()):
|
elif any(expr.startswith(op) for op in RExpr.uops.keys()):
|
||||||
# sort by len to avoid ambiguities
|
# sort by len to avoid ambiguities
|
||||||
for op in sorted(RExpr.uops.keys(), reverse=True):
|
for op in sorted(RExpr.uops.keys(), reverse=True):
|
||||||
if expr.startswith(op):
|
if expr.startswith(op):
|
||||||
a, tail = p_expr(expr[len(op):].lstrip(), mt.inf())
|
a, tail = p_expr(expr[len(op):].lstrip(), mt.inf)
|
||||||
a = RExpr.uops[op](a)
|
a = RExpr.uops[op](a)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@@ -712,10 +938,10 @@ class RExpr:
|
|||||||
if tail:
|
if tail:
|
||||||
raise RExpr.Error("trailing expr? %s" % tail)
|
raise RExpr.Error("trailing expr? %s" % tail)
|
||||||
|
|
||||||
except RExpr.Error as e:
|
except (RExpr.Error, ValueError) as e:
|
||||||
print('error: in expr: %s' % self.expr,
|
print('error: in expr: %s' % self.expr,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
print('error: %s' % e.reason,
|
print('error: %s' % e,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
|
||||||
@@ -726,7 +952,7 @@ class RExpr:
|
|||||||
except RExpr.Error as e:
|
except RExpr.Error as e:
|
||||||
print('error: in expr: %s' % self.expr,
|
print('error: in expr: %s' % self.expr,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
print('error: %s' % e.reason,
|
print('error: %s' % e,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
|
||||||
@@ -737,7 +963,7 @@ class RExpr:
|
|||||||
except RExpr.Error as e:
|
except RExpr.Error as e:
|
||||||
print('error: in expr: %s' % self.expr,
|
print('error: in expr: %s' % self.expr,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
print('error: %s' % e.reason,
|
print('error: %s' % e,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
|
||||||
@@ -748,7 +974,7 @@ class RExpr:
|
|||||||
except RExpr.Error as e:
|
except RExpr.Error as e:
|
||||||
print('error: in expr: %s' % self.expr,
|
print('error: in expr: %s' % self.expr,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
print('error: %s' % e.reason,
|
print('error: %s' % e,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
|
||||||
@@ -759,7 +985,7 @@ class RExpr:
|
|||||||
except RExpr.Error as e:
|
except RExpr.Error as e:
|
||||||
print('error: in expr: %s' % self.expr,
|
print('error: in expr: %s' % self.expr,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
print('error: %s' % e.reason,
|
print('error: %s' % e,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
|
||||||
@@ -1299,50 +1525,6 @@ if __name__ == "__main__":
|
|||||||
'-Y', '--summary',
|
'-Y', '--summary',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help="Only show the total.")
|
help="Only show the total.")
|
||||||
parser.add_argument(
|
|
||||||
'--int',
|
|
||||||
action='append',
|
|
||||||
help="Treat these fields as ints.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--float',
|
|
||||||
action='append',
|
|
||||||
help="Treat these fields as floats.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--frac',
|
|
||||||
action='append',
|
|
||||||
help="Treat these fields as fractions.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--sum',
|
|
||||||
action='append',
|
|
||||||
help="Add these fields (the default).")
|
|
||||||
parser.add_argument(
|
|
||||||
'--prod',
|
|
||||||
action='append',
|
|
||||||
help="Multiply these fields.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--min',
|
|
||||||
action='append',
|
|
||||||
help="Take the minimum of these fields.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--max',
|
|
||||||
action='append',
|
|
||||||
help="Take the maximum of these fields.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--avg', '--mean',
|
|
||||||
action='append',
|
|
||||||
help="Average these fields.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--stddev',
|
|
||||||
action='append',
|
|
||||||
help="Find the standard deviation of these fields.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--gmean',
|
|
||||||
action='append',
|
|
||||||
help="Find the geometric mean of these fields.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--gstddev',
|
|
||||||
action='append',
|
|
||||||
help="Find the geometric standard deviation of these fields.")
|
|
||||||
sys.exit(main(**{k: v
|
sys.exit(main(**{k: v
|
||||||
for k, v in vars(parser.parse_intermixed_args()).items()
|
for k, v in vars(parser.parse_intermixed_args()).items()
|
||||||
if v is not None}))
|
if v is not None}))
|
||||||
|
|||||||
Reference in New Issue
Block a user