expr: error on bad operator usage, add bad operator alternative functions, implement item slicing correctly
This commit is contained in:
parent
9c1aa11e50
commit
822eaedf0b
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
142
sx/core/expr.py
142
sx/core/expr.py
|
@ -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]:
|
||||
|
|
Loading…
Reference in New Issue