expr: error on bad operator usage, add bad operator alternative functions, implement item slicing correctly

This commit is contained in:
Shiz 2021-08-17 03:09:07 +02:00
parent 9c1aa11e50
commit 822eaedf0b
3 changed files with 111 additions and 39 deletions

View File

@ -1,7 +1,7 @@
from .core import parse, dump, sizeof, offsetof, default, context
from .core.base import Params, Context, Type, to_type, Error
from .core.io import Stream, Segment, to_stream, Endian, BitAlignment
from .core.expr import BaseExpr, Expr, const, infer
from .core.expr import BaseExpr, Expr, const, infer, bool_, not_, and_, or_, in_, len_, int_, float_
from .core.meta import Wrapper, Generic
del core

View File

@ -161,19 +161,19 @@ class Context:
def get(self, value: U[T, PossibleDynamic[T]]) -> T:
from .expr import Expr, get
if isinstance(value, Expr):
if isinstance(value, (Expr, FunctionType, tuple)):
value = get(value)
return cast(T, value)
def peek(self, value: U[T, PossibleDynamic[T]]) -> O[T]:
from .expr import Expr, peek
if isinstance(value, Expr):
if isinstance(value, (Expr, FunctionType, tuple)):
value = peek(value)
return cast(T, value)
def put(self, value: U[T, PossibleDynamic[T]], new: T) -> None:
from .expr import Expr, put
if isinstance(value, Expr):
if isinstance(value, (Expr, FunctionType, tuple)):
put(value, new)

View File

@ -1,6 +1,7 @@
import math
import operator
import functools
import types
from typing import Any, Optional as O, Union as U, Sequence, Mapping, Callable, Generic as G, TypeVar, List
@ -51,6 +52,8 @@ reverse = {
}
T = TypeVar('T')
V = TypeVar('V')
X = TypeVar('X')
class BaseExpr(G[T]):
@ -66,6 +69,13 @@ class BaseExpr(G[T]):
def _sx_is_const_(self) -> bool:
raise NotImplementedError
def raise_type_error(self, op, *y, alternatives=[]):
if not alternatives:
alternatives = [op + ('_(x, y)' if y else '_(x)')]
msg = f'bad operator __{op}__ called on Expr, try {" / ".join(alternatives)}; or use lambdas for calling standard functions!'
raise TypeError(msg)
class Expr(G[T], BaseExpr[T]):
def __getattr__(self, name: str) -> 'AttrExpr':
return AttrExpr(self, name)
@ -76,30 +86,31 @@ class Expr(G[T], BaseExpr[T]):
def __call__(self, *args: Any, **kwargs: Any) -> 'CallExpr':
return CallExpr(self, args, kwargs)
for x, y in {'bool': ['bool_(x)', 'not_(x, y)', 'and_(x, y)', 'or_(x, y)', 'in_(x, y)'], 'contains': ['in_(x, y)'], 'int': [], 'float': [], 'len': []}.items():
locals()['__' + x + '__'] = functools.partialmethod(raise_type_error, op=x, alternatives=y)
for x in ('lt', 'le', 'eq', 'ne', 'ge', 'gt'):
locals()['__' + x.strip('_') + '__'] = functools.partialmethod(lambda self, x, other: CompExpr(getattr(operator, x), self, other), x)
for x in ('not_', 'truth', 'abs', 'index', 'inv', 'neg', 'pos'):
for x in ('abs', 'invert', 'neg', 'pos'):
locals()['__' + x.strip('_') + '__'] = functools.partialmethod(lambda self, x: UnaryExpr(getattr(operator, x), self), x)
for x in (
'add', 'and_', 'floordiv', 'lshift', 'mod', 'mul', 'matmul', 'or_', 'pow', 'rshift', 'sub', 'truediv', 'xor',
'concat', 'contains',
):
locals()[ '__' + x.strip('_') + '__'] = functools.partialmethod(lambda self, x, other: BinExpr(getattr(operator, x), self, other), x)
locals()['__r' + x.strip('_') + '__'] = functools.partialmethod(lambda self, x, other: BinExpr(getattr(operator, x), other, self), x)
del x
del x, y
class AttrExpr(G[T], Expr[T]):
def __init__(self, parent: BaseExpr, attr: str) -> None:
class AttrExpr(G[T, V], Expr[V]):
def __init__(self, parent: BaseExpr[T], attr: str) -> None:
self.__parent = parent
self.__attr = attr
def _sx_get_(self, pop: bool = True) -> T:
def _sx_get_(self, pop: bool = True) -> V:
return getattr(get(self.__parent, pop=pop), get(self.__attr, pop=pop))
def _sx_peek_(self, pop: bool = True) -> T:
def _sx_peek_(self, pop: bool = True) -> V:
return getattr(peek(self.__parent, pop=pop), peek(self.__attr, pop=pop))
def _sx_put_(self, value: T, pop: bool = True) -> None:
def _sx_put_(self, value: V, pop: bool = True) -> None:
parent = get(self.__parent, pop=False)
setattr(parent, get(self.__attr, pop=pop), value)
put(self.__parent, parent, pop=pop)
@ -113,44 +124,60 @@ class AttrExpr(G[T], Expr[T]):
def __repr__(self) -> str:
return f'{self.__parent!r}.{self.__attr}'
class ItemExpr(G[T], Expr[T]):
def __init__(self, parent: BaseExpr, item: Any) -> None:
class ItemExpr(G[T, V], Expr[V]):
def __init__(self, parent: BaseExpr[T], item: Any) -> None:
self.__parent = parent
self.__item = item
def _sx_get_(self, pop: bool = True) -> T:
return get(self.__parent, pop=pop)[get(self.__item, pop=pop)]
def _sx_get_(self, pop: bool = True) -> V:
if isinstance(self.__item, slice):
item = slice(get(self.__item.start, pop=pop), get(self.__item.stop, pop=pop), get(self.__item.step, pop=pop))
else:
item = get(self.__item, pop=pop)
return get(self.__parent, pop=pop)[item]
def _sx_peek_(self, pop: bool = True) -> T:
return peek(self.__parent, pop=pop)[peek(self.__item, pop=pop)]
def _sx_peek_(self, pop: bool = True) -> V:
if isinstance(self.__item, slice):
item = slice(peek(self.__item.start, pop=pop), peek(self.__item.stop, pop=pop), peek(self.__item.step, pop=pop))
else:
item = peek(self.__item, pop=pop)
return peek(self.__parent, pop=pop)[item]
def _sx_put_(self, value: T, pop: bool = True) -> None:
def _sx_put_(self, value: V, pop: bool = True) -> None:
parent = get(self.__parent, pop=False)
parent[get(self.__item, pop=pop)] = value
if isinstance(self.__item, slice):
item = slice(get(self.__item.start, pop=pop), get(self.__item.stop, pop=pop), get(self.__item.step, pop=pop))
else:
item = get(self.__item, pop=pop)
parent[item] = value
put(self.__parent, parent, pop=pop)
def _sx_is_const_(self) -> bool:
return is_const(self.__parent) and is_const(self.__item)
if isinstance(self.__item, slice):
item_const = is_const(self.__item.start) and is_const(self.__item.stop) and is_const(self.__item.step)
else:
item_const = is_const(self.__item)
return is_const(self.__parent) and item_const
def __repr__(self) -> str:
def __str__(self) -> str:
return f'{self.__parent}[{self.__item}]'
def __repr__(self) -> str:
return f'{self.__parent!r}[{self.__item!r}]'
class CallExpr(G[T], Expr[T]):
def __init__(self, parent: BaseExpr, args: Sequence[Any], kwargs: Mapping[str, Any]) -> None:
class CallExpr(G[T, V], Expr[V]):
def __init__(self, parent: BaseExpr[T], args: Sequence[Any], kwargs: Mapping[str, Any]) -> None:
self.__parent = parent
self.__args = args
self.__kwargs = kwargs
def _sx_get_(self, pop: bool = True) -> T:
def _sx_get_(self, pop: bool = True) -> V:
return get(self.__parent, pop=pop)(*(get(a, pop=pop) for a in self.__args), **{k: get(v, pop=pop) for k, v in self.__kwargs.items()})
def _sx_peek_(self, pop: bool = True) -> T:
def _sx_peek_(self, pop: bool = True) -> V:
return peek(self.__parent, pop=pop)(*(peek(a, pop=pop) for a in self.__args), **{k: peek(v, pop=pop) for k, v in self.__kwargs.items()})
def _sx_put_(self, value: T, pop: bool = True) -> None:
def _sx_put_(self, value: V, pop: bool = True) -> None:
raise NotImplementedError(f'{self.__class__.__name__} is not invertible')
def _sx_is_const_(self) -> bool:
@ -169,18 +196,18 @@ class CallExpr(G[T], Expr[T]):
return f'{self.__parent!r}({a})'
class UnaryExpr(G[T], Expr[T]):
def __init__(self, op: Callable[[Any], T], value: BaseExpr) -> None:
class UnaryExpr(G[T, V], Expr[V]):
def __init__(self, op: Callable[[T], V], value: BaseExpr[T]) -> None:
self.__op = op
self.__value = value
def _sx_get_(self, pop: bool = True) -> T:
def _sx_get_(self, pop: bool = True) -> V:
return self.__op(get(self.__value, pop=pop))
def _sx_peek_(self, pop: bool = True) -> T:
def _sx_peek_(self, pop: bool = True) -> V:
return self.__op(peek(self.__value, pop=pop))
def _sx_put_(self, value: T, pop: bool = True) -> None:
def _sx_put_(self, value: V, pop: bool = True) -> None:
if self.__op not in reverse:
raise NotImplementedError(f'{self.__class__.__name__} {symbols[self.__op]!r} is not invertible')
put(self.__value, reverse[self.__op](value), pop=pop)
@ -194,19 +221,19 @@ class UnaryExpr(G[T], Expr[T]):
def __repr__(self) -> str:
return f'({symbols[self.__op]}{self.__value!r})'
class BinExpr(G[T], Expr[T]):
def __init__(self, op: Callable[[Any, Any], T], left: BaseExpr, right: BaseExpr) -> None:
class BinExpr(G[T, V, X], Expr[X]):
def __init__(self, op: Callable[[T, V], X], left: BaseExpr[T], right: BaseExpr[V]) -> None:
self.__op = op
self.__left = left
self.__right = right
def _sx_get_(self, pop: bool = True) -> T:
def _sx_get_(self, pop: bool = True) -> X:
return self.__op(get(self.__left, pop=pop), get(self.__right, pop=pop))
def _sx_peek_(self, pop: bool = True) -> T:
def _sx_peek_(self, pop: bool = True) -> X:
return self.__op(peek(self.__left, pop=pop), peek(self.__right, pop=pop))
def _sx_put_(self, value: T, pop: bool = True) -> None:
def _sx_put_(self, value: X, pop: bool = True) -> None:
if is_const(self.__left):
operand = self.__left
target = self.__right
@ -233,8 +260,8 @@ class BinExpr(G[T], Expr[T]):
def __repr__(self) -> str:
return f'({self.__left!r} {symbols[self.__op]} {self.__right!r})'
class CompExpr(Expr[bool]):
def __init__(self, op: Callable[[Any, Any], bool], left: BaseExpr, right: BaseExpr) -> None:
class CompExpr(G[T, V], Expr[bool]):
def __init__(self, op: Callable[[T, V], bool], left: BaseExpr[T], right: BaseExpr[V]) -> None:
self.__op = op
self.__left = left
self.__right = right
@ -326,23 +353,68 @@ class ConstChangeExpr(G[T], Expr[T]):
return f'infer({self.__child})'
def bool_(x: BaseExpr[T]) -> UnaryExpr[T, bool]:
return UnaryExpr(bool, x)
def not_(x: BaseExpr[T]) -> UnaryExpr[T, bool]:
return UnaryExpr(lambda a: not a, x)
def and_(x: BaseExpr[T], y: BaseExpr[V]) -> BinExpr[T, V, bool]:
return BinExpr(lambda a, b: a and b, x, y)
def or_(x: BaseExpr[T], y: BaseExpr[V]) -> BinExpr[T, V, bool]:
return BinExpr(lambda a, b: a or b, x, y)
def in_(x: BaseExpr[T], y: BaseExpr[V]) -> BinExpr[T, V, bool]:
return BinExpr(lambda a, b: a in b, x, y)
def len_(x: BaseExpr[T]) -> UnaryExpr[T, int]:
return UnaryExpr(len, x)
def int_(x: BaseExpr[T]) -> UnaryExpr[T, int]:
return UnaryExpr(int, x)
def float_(x: BaseExpr[T]) -> UnaryExpr[T, float]:
return UnaryExpr(float, x)
def get(expr: U[T, BaseExpr[T]], pop: bool = True) -> T:
if isinstance(expr, BaseExpr):
return expr._sx_get_(pop=pop)
if isinstance(expr, types.FunctionType):
return expr(pop)
if isinstance(expr, tuple) and len(expr) >= 2:
_get, _put = expr[:2]
return _get(pop)
return expr
def peek(expr: U[T, BaseExpr[T]], pop: bool = True) -> T:
if isinstance(expr, BaseExpr):
return expr._sx_peek_(pop=pop)
if isinstance(expr, types.FunctionType):
return expr(pop)
if isinstance(expr, tuple) and len(expr) >= 2:
if len(expr) == 2:
_peek = expr[0]
else:
_get, _put, _peek = expr[:3]
return _peek(pop)
return expr
def put(expr: U[T, BaseExpr[T]], value: T, pop: bool = True) -> None:
if isinstance(expr, BaseExpr):
expr._sx_put_(value, pop=pop)
if isinstance(expr, tuple) and len(expr) >= 2:
_get, _put, _peek = expr[:2]
return _put(value, pop)
def is_const(expr: U[T, BaseExpr[T]]) -> bool:
if isinstance(expr, BaseExpr):
return expr._sx_is_const_()
if isinstance(expr, types.FunctionType):
return False
if isinstance(expr, tuple) and isinstance(expr[0], types.FunctionType):
return False
return True
def const(expr: U[T, BaseExpr[T]]) -> ConstChangeExpr[T]: