123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- import logging
-
- from .rpnlib import *
-
- logger = logging.getLogger()
-
- class Turmit(object):
- ''' @brief Represent a turmit that act given an RPN expression with an
- infinite looping stack with variable stack size.
- '''
-
- def __init__(self, stack_size=8, **kwargs):
- ''' @brief Instanciated a new Turmit with a programm and a memory stack
- @param stack_size int : stack_size
- @param prog str | RpnExpr | None : turmit programm if None generate
- it randomlt
- @param prog_size int : if prog is generated randomly it will make
- this size
- @param max_int int : maximum integer value + 1
- @param signed_int bool : if True integers are signed
- @throw ValueError for bad option value
- @throw RuntimError for bad arguments
- '''
- if stack_size < 2:
- msg = 'Stack size has to be >= 2 but %d given' % (stack_size)
- raise ValueError(msg)
-
- ## @brief List that represent a stack
- self._stack = [ 0 for _ in range(stack_size)]
- ## @brief Stack head index
- self._cur = stack_size - 1
- ## @brief Stores turmit direction
- self._dir = 0
- ## @brief Stores turmit program
- self._prog = None
- self._prog_sz = 42
-
- ## @brief Stores maximum int value + 1
- # @note This limit is handled by @ref rpnlib.RpnOp()
- self._max_int = 0x10000
- ## @brief If True integers are signed
- # @note This limit is handled by @ref rpnlib.RpnOp()
- self._signed_int = True
-
- if 'max_int' in kwargs:
- v = kwargs['max_int']
- if v < 1:
- raise ValueError('Maximum int have to be >= 1 but %d found' % v)
- self._max_int = kwargs['max_int']
- del(kwargs['max_int'])
- if 'signed_int' in kwargs:
- self._signed_int = bool(kwargs['signed_int'])
- del(kwargs['signed_int'])
- if 'prog_size' in kwargs:
- v = kwargs['prog_size']
- if v < 2:
- raise ValueError('Prog_size have to be >= 2 but %d found' % v)
- self._prog_sz = v
- del(kwargs['prog_size'])
- if 'prog' in kwargs:
- v = kwargs['prog']
- if isinstance(v, str):
- v = RpnExpr.from_string(v)
- if isinstance(v, RpnExpr):
- self._prog = v
- else:
- msg = 'Str or RpnExpr expected but %s found' % (type(v))
- raise TypeError(msg)
- del(kwargs['prog'])
- if len(kwargs) > 0:
- msg = 'Unexpected arguments : [%s]' % ', '.join(kwargs.keys())
- raise RuntimeError(msg)
-
- if self._prog is None:
- self._prog = RpnExpr.random(self._prog_sz)
-
- self._determin_score = 5
-
- self.__expr_fun = lambda *args, **kwargs: (0, True, None)
-
- def __call__(self, **context):
- ''' @brief Exec the RPN expression and return the stack head
- @param context dict : dict with variable values (see @ref rpnlib._var_list)
- @return stack head after RPN
- '''
- context = {k:IntVar(context[k]) % self._max_int for k in context}
-
- if self.__expr_fun is None:
- self._compile()
- ret = self.__expr_fun(self, **context)
-
- determinist = ret[1]
-
- if len(ret) == 2:
- ret = ret[0]
- return ret
- #if len(ret) == 3 -> returned a continuation address in RPN expr
-
- #self.__ip = 0
- self.__ip = ret[0]
-
- while self.__ip < len(self._prog):
- sym = self._prog[self.__ip]
- if sym.optype == RpnSymbol.VALUE:
- self._push(sym.value)
- elif sym.optype == RpnSymbol.VARIABLE:
- self._push(IntVar(context[sym.value.lower()]))
- elif sym.optype == RpnSymbol.OPERATION:
- op = getattr(self, sym.value.lower())
- if determinist:
- if not op():
- determinist = False
- else:
- op()
- self.__ip += 1
-
- self._determin_score += 0 if determinist else 1
- return self.shead
-
- def _compile(self):
- code = '''def expr_fun(self, x, y, r, g, b):
-
- determinist = True'''
-
- op2_head = '''a = self._stack[self._cur]
- self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
- if isinstance(a, IntVar) or isinstance(self._stack[self._cur], IntVar):
- determinist = False
- '''
- for ip, sym in enumerate(self._prog):
- if sym.optype == RpnSymbol.VALUE:
- code += '''
- self._cur += 1
- self._cur %%= len(self._stack)
- self._stack[self._cur] = %d
- ''' % sym.value
- elif sym.optype == RpnSymbol.VARIABLE:
- code += '''
- self._cur += 1
- self._cur %%= len(self._stack)
- self._stack[self._cur] = %s
- ''' % sym.value.lower()
- elif sym.value == 'add':
- code += '''
- %s
- self._stack[self._cur] += a
- self._stack[self._cur] %= self._max_int
- ''' % op2_head
- elif sym.value == 'sub':
- code += '''
- %s
- self._stack[self._cur] = self._stack[self._cur] - a
- if self._stack[self._cur] < 0:
- self._stack[self._cur] += self._max_int
- ''' % op2_head
- elif sym.value == 'mul':
- code += '''
- %s
- self._stack[self._cur] *= a
- self._stack[self._cur] %= self._max_int
- ''' % op2_head
- elif sym.value == 'div':
- code += '''
- %s
- if a != 0:
- self._stack[self._cur] = self._stack[self._cur] // a
- ''' % op2_head
- elif sym.value == 'mod':
- code += '''
- %s
- if a != 0:
- self._stack[self._cur] = self._stack[self._cur] % a
- ''' % op2_head
- elif sym.value == 'bin_and':
- code += '''
- %s
- self._stack[self._cur] &= a
- ''' % op2_head
- elif sym.value == 'bin_or':
- code += '''
- %s
- self._stack[self._cur] |= a
- ''' % op2_head
- elif sym.value == 'bin_xor':
- code += '''
- %s
- self._stack[self._cur] ^= a
- ''' % op2_head
- elif sym.value == 'lshift':
- code += '''
- %s
- self._stack[self._cur] = (self._stack[self._cur] << a) % self._max_int
- ''' % op2_head
- elif sym.value == 'rshift':
- code += '''
- %s
- self._stack[self._cur] = self._stack[self._cur] >> a
- ''' % op2_head
- elif sym.value == 'dup':
- code += '''
- a = self._stack[self._cur]
- if isinstance(a, IntVar):
- determinist = False
- self._cur += 1
- self._cur %= len(self._stack)
- self._stack[self._cur] = a
- '''
- elif sym.value == 'pop':
- code += '''
- self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
- '''
- else:
- code += '''
- return (%d,determinist, None)
- ''' % ip
- code += '''
- return (self._stack[self._cur], determinist)
- '''
- g = {}
- l = {}
- exec(code, g, l)
- self.__expr_fun = l['expr_fun']
-
- @property
- def shead(self):
- ''' @brief Stack head attribute '''
- return self._stack[self._cur]
-
- def _pop(self):
- ''' @brief Pops a value from the stack
-
- @note If stack head is 0, set the stack head to len(self._stack)
- - 1
-
- @return popped value
- '''
- res = self._stack[self._cur]
- if self._cur <= 0:
- self._cur = len(self._stack) - 1
- else:
- self._cur -= 1
- return res
-
- def _push(self, val):
- ''' @brief Pushes a value on the stack
-
- @note If no space left on the stack, the head is set to 0
-
- @param val int : value to push
- '''
- self._cur += 1
- if self._cur >= len(self._stack) or abs(self._cur) >= len(self._stack):
- self._cur = 0
- self._stack[self._cur] = self._cast_int(val)
-
- def _cast_int(self, val):
- ''' @brief Transform integer given Turmit max int and signed_int
- attr
- @param val int : integer to transform
- @return integer
- '''
- intvar = isinstance(val, IntVar)
- val = int(val)
- if self._max_int is not None:
- val %= self._max_int
- if not self._signed_int and val < 0:
- val = self._max_int - val
- return IntVar(val) if intvar else val
-
- @RpnOp
- def mem_sz(self, new_sz):
- ''' @brief Update memory stack size
- '''
-
- stksz = len(self._stack)
- new_sz %= 0xFFF
- if new_sz < 2:
- new_sz = 2
- if new_sz > stksz:
- self._stack += [ 0 for _ in range(new_sz - stksz) ]
- elif new_sz < stksz:
- self._stack = self._stack[0:new_sz]
- if self._cur >= new_sz:
- self._cur = 0
-
- @RpnOp
- def add(self, a, b):
- ''' @brief Adds one value to another
- @param a int : value
- @param b int : value to add to a
- @return a + b
- '''
- return a + b
-
- @RpnOp
- def sub(self, a, b):
- ''' @brief Substitutes a value to another
- @param a int : value
- @param b int : value to substitute to a
- @return a - b
- '''
- return a - b
-
- @RpnOp
- def bin_and(self, a, b):
- ''' @brief Binary and
- @param a int : value
- @param b int : value
- @return a & b
- '''
- return a & b
-
- @RpnOpNoMod
- def dup(self, a):
- ''' @brief Duplicates stack head
- @param a int : value
- @return a
- '''
- self._push(a)
- return a
-
- @RpnOp
- def lshift(self, a, b):
- ''' @brief Left shifts a of b
- @param a int : value
- @param b int : value
- @return a << b
- '''
- return a << b
-
- @RpnOp
- def mod(self, a, b):
- ''' @brief Gives a modulo b
- @param a int : value
- @param b int : value
- @return a % b
- '''
- return a % b
-
-
- @RpnOp
- def mul(self, a, b):
- ''' @brief Multiplies a with b
- @param a int : value
- @param b int : value
- @return a * b
- '''
- return a * b
-
- @RpnOp
- def div(self, a, b):
- ''' @brief Divides a with b
- @param a int : value
- @param b int : value
- @return a // b
- '''
- return a // b
-
- @RpnOp
- def bin_or(self, a, b):
- ''' @brief Binary or
- @param a int : value
- @param b int : value
- @return a | b
- '''
- return a | b
-
- @RpnOp
- def bin_xor(self, a, b):
- ''' @brief Binary xor
- @param a int : value
- @param b int : value
- @return a ^ b
- '''
- return a ^ b
-
- @RpnOpNoMod
- def pop(self, a):
- ''' @brief Pops a, head of the stack
- @param a int : value
- '''
- pass
-
- @RpnOpNoMod
- def swp(self, a, b):
- ''' @brief Swap the two values on top of the stack
- @param a int : v1
- @param b int : v2
- '''
- self._push(b)
- self._push(a)
-
- @RpnOp
- def jmp(self, offset):
- ''' @brief Increments IP
- @param offset int : jump offset symbols
- '''
- self.__ip += offset
-
- @RpnOp
- def jz(self, a, offset):
- ''' @brief Increments IP if a == 0
- @param a int : operand
- @param offset int : jump offset symbols
- '''
- if a == 0:
- self.__ip += offset
-
- """
- @RpnOp
- def jnz(self, a, offset):
- ''' @brief Increments IP if a != 0
- @param a int : operand
- @param offset int : jump offset symbols
- '''
- if a != 0:
- self.__ip += offset
- """
-
- @RpnOp
- def jcmp(self, a, b, cnd, offset):
- ''' @brief Increments IP if a == b
-
- Condition type is encoded on an int :
- - 0b0000 : je
- - 0b0001 : jne
- - 0b0010 : jle
- - 0b0011 : jl
- - 0b0100 : jg
- - 0b0101 : jge
- - 0b0110 : jne
- - 0b0111 : jmp
-
- @param a int : operand
- @param b int : operand
- @param cnd int : condition type
- @param offset int : jump offset symbols
- '''
- jmp = False
- if cnd & 1 == 0 and a == b or cnd & 1 and a != b or\
- cnd & 0b10 and a < b or cnd & 0b100 and a > b:
- self.__ip += offset
|