expr: add const-ness attribute and const()/infer() markers

This commit is contained in:
Shiz 2021-06-28 05:37:51 +02:00
parent a6eb428411
commit 11c556d7d6
3 changed files with 74 additions and 10 deletions

View File

@ -2,7 +2,7 @@ from .core import parse, dump, sizeof, offsetof, default, to_type, context
from .core.base import Params, Context, Type, Error
from .core.io import Stream, Segment
from .core.meta import Wrapper, Generic
from .core.expr import BaseExpr, Expr
from .core.expr import BaseExpr, Expr, const, infer
from .types.data import Nothing, Static, Ignored, Data, data
from .types.num import *
@ -14,7 +14,7 @@ from .types.control import Switch, If
from .types.io import AlignTo, AlignedTo
__all__ = [x.__name__ for x in {
parse, dump, sizeof, offsetof, default, to_type,
parse, dump, sizeof, offsetof, default, to_type, const, infer,
Params, Context, Type, Error, Stream, Segment, BaseExpr, Expr,
Wrapper, Default, Sized, Ref, Transform, Mapped, Enum, Check, Fixed,

View File

@ -1,7 +1,7 @@
import math
import operator
import functools
from typing import Any, Optional as O, Sequence, Mapping, Callable, Generic as G, TypeVar, List
from typing import Any, Optional as O, Union as U, Sequence, Mapping, Callable, Generic as G, TypeVar, List
symbols = {
@ -63,6 +63,9 @@ class BaseExpr(G[T]):
def _sx_put_(self, value: T, pop: bool = True) -> None:
raise NotImplementedError
def _sx_is_const_(self) -> bool:
raise NotImplementedError
class Expr(G[T], BaseExpr[T]):
def __getattr__(self, name: str) -> 'AttrExpr':
return AttrExpr(self, name)
@ -101,6 +104,9 @@ class AttrExpr(G[T], Expr[T]):
setattr(parent, get(self.__attr, pop=pop), value)
put(self.__parent, parent, pop=pop)
def _sx_is_const_(self) -> bool:
return is_const(self.__parent) and is_const(self.__attr)
def __str__(self) -> str:
return f'{self.__parent}.{self.__attr}'
@ -123,6 +129,9 @@ class ItemExpr(G[T], Expr[T]):
parent[get(self.__item, pop=pop)] = value
put(self.__parent, parent, pop=pop)
def _sx_is_const_(self) -> bool:
return is_const(self.__parent) and is_const(self.__item)
def __repr__(self) -> str:
return f'{self.__parent}[{self.__item}]'
@ -144,6 +153,9 @@ class CallExpr(G[T], Expr[T]):
def _sx_put_(self, value: T, pop: bool = True) -> None:
raise NotImplementedError(f'{self.__class__.__name__} is not invertible')
def _sx_is_const_(self) -> bool:
return False
def __str__(self) -> str:
args = [repr(a) for a in self.__args]
args += [f'{k}: {v}' for k, v in self.__kwargs.items()]
@ -173,6 +185,9 @@ class UnaryExpr(G[T], Expr[T]):
raise NotImplementedError(f'{self.__class__.__name__} {symbols[self.__op]!r} is not invertible')
put(self.__value, reverse[self.__op](value), pop=pop)
def _sx_is_const_(self) -> bool:
return is_const(self.__value)
def __str__(self) -> str:
return f'({symbols[self.__op]}{self.__value})'
@ -192,22 +207,25 @@ class BinExpr(G[T], Expr[T]):
return self.__op(peek(self.__left, pop=pop), peek(self.__right, pop=pop))
def _sx_put_(self, value: T, pop: bool = True) -> None:
if not isinstance(self.__left, BaseExpr):
if is_const(self.__left):
operand = self.__left
target = self.__right
i = 0
elif not isinstance(self.__right, BaseExpr):
elif is_const(self.__right):
operand = self.__right
target = self.__left
i = 1
else:
raise NotImplementedError(f'{self.__class__.__name__} has two expression operands and is not invertible')
raise NotImplementedError(f'{self.__class__.__name__} has two non-const expression operands and is not invertible')
if self.__op not in reverse:
raise NotImplementedError(f'{self.__class__.__name__} {symbols[self.__op]!r} is not invertible')
rev = reverse[self.__op]
if isinstance(rev, tuple):
rev = rev[i]
put(target, rev(value, operand), pop=pop)
put(target, rev(value, get(operand, pop=pop)), pop=pop)
def _sx_is_const_(self) -> bool:
return is_const(self.__left) and is_const(self.__right)
def __str__(self) -> str:
return f'({self.__left} {symbols[self.__op]} {self.__right})'
@ -245,6 +263,9 @@ class CompExpr(Expr[bool]):
raise NotImplementedError(f'{self.__class__.__name__} {symbols[self.__op]!r} is not invertible')
put(target, value, pop=pop)
def _sx_is_const_(self) -> bool:
return is_const(self.__left) and is_const(self.__right)
def __str__(self) -> str:
return f'({self.__left} {symbols[self.__op]} {self.__right})'
@ -271,23 +292,60 @@ class ProxyExpr(G[T], Expr[T]):
def _sx_put_(self, value: T, pop: bool = True) -> None:
return put(self.__stack[-1], value, pop=pop)
def _sx_is_const_(self) -> bool:
return is_const(self.__stack[-1])
def __str__(self) -> str:
return f'${self.__name}'
def __repr__(self) -> str:
return f'${self.__name}(=> {self.__stack!r})'
class ConstChangeExpr(G[T], Expr[T]):
def __init__(self, child: BaseExpr[T], const: bool = True) -> None:
self.__child = child
self.__const = const
def get(expr: Any, pop: bool = True) -> Any:
def _sx_get_(self, pop: bool = True) -> T:
return get(self.__child, pop=pop)
def _sx_peek_(self, pop: bool = True) -> T:
return peek(self.__child, pop=pop)
def _sx_put_(self, value: T, pop: bool = True) -> None:
return put(self.__child, value, pop=pop)
def _sx_is_const_(self) -> bool:
return self.__const
def __str__(self) -> str:
if self.__const:
return f'const({self.__child})'
else:
return f'infer({self.__child})'
def get(expr: U[T, BaseExpr[T]], pop: bool = True) -> T:
if isinstance(expr, BaseExpr):
return expr._sx_get_(pop=pop)
return expr
def peek(expr: Any, pop: bool = True) -> Any:
def peek(expr: U[T, BaseExpr[T]], pop: bool = True) -> T:
if isinstance(expr, BaseExpr):
return expr._sx_peek_(pop=pop)
return expr
def put(expr: Any, value: Any, pop: bool = True) -> None:
def put(expr: U[T, BaseExpr[T]], value: T, pop: bool = True) -> None:
if isinstance(expr, BaseExpr):
expr._sx_put_(value, pop=pop)
def is_const(expr: U[T, BaseExpr[T]]) -> bool:
if isinstance(expr, BaseExpr):
return expr._sx_is_const_()
return True
def const(expr: U[T, BaseExpr[T]]) -> ConstChangeExpr[T]:
return ConstChangeExpr(expr, const=True)
def infer(expr: U[T, BaseExpr[T]]) -> ConstChangeExpr[T]:
return ConstChangeExpr(expr, const=False)

View File

@ -55,6 +55,9 @@ class Generic(G[T], Type[T], BaseExpr[T]):
def _sx_peek_(self, pop: bool = False) -> T:
return self.stack[-1]
def _sx_is_const_(self) -> bool:
return False
def _get_sx_type_(self, ident: Any) -> Type:
return to_type(self.stack[-1])
@ -142,3 +145,6 @@ class TypeSource(G[T], Wrapper[T], BaseExpr[T]):
context, segment, stream, pos, _ = self.stack.pop() if pop else self.stack[-1]
with context.enter_segment(segment, stream, pos, os.SEEK_SET) as f:
context.dump(to_type(self.child), f, value)
def _sx_is_const_(self) -> bool:
return False