Explorar el Código

Implement an RpnExpr class + add more options to Turmits

Adding prog attribute to Turmit (storing a RpnExpr)
Yann Weber hace 6 años
padre
commit
a9715d9821
Se han modificado 5 ficheros con 203 adiciones y 5 borrados
  1. 78
    2
      gte/rpnlib.py
  2. 64
    2
      gte/turmit.py
  3. 27
    0
      tests/test_rpnexpr.py
  4. 4
    0
      tests/test_rpnsymbol.py
  5. 30
    1
      tests/test_turmit.py

+ 78
- 2
gte/rpnlib.py Ver fichero

@@ -31,6 +31,10 @@ def RpnOp(method):
31 31
                 if not isinstance(topush, int):
32 32
                     raise ValueError('Turmit.%s() returned a list containing a\
33 33
  %s : %s' % (method.__name__, type(topush), topush))
34
+                if self._max_int is not None:
35
+                    topush %= self._max_int
36
+                    if not self._signed_int and topush < 0:
37
+                        topush = self._max_int - topush
34 38
                 self._push(topush)
35 39
         except TypeError:
36 40
             if not isinstance(res, int):
@@ -41,6 +45,34 @@ def RpnOp(method):
41 45
     _op_list[method.__name__] = (method, wrapped)
42 46
     return wrapped
43 47
 
48
+class RpnExpr(list):
49
+    ''' @brief Extended list that only contains RpnSymbol '''
50
+    
51
+    def __init__(self, *args, **kwargs):
52
+        super().__init__(*args, **kwargs)
53
+        for i, item in enumerate(self):
54
+            if not isinstance(item, RpnSymbol):
55
+                raise TypeError('Item %d is %s but RpnSymbol expected' % (i,
56
+                                                                          item))
57
+    def __setitem__(self, idx, val):
58
+        if not isinstance(val, RpnSymbol):
59
+            raise TypeError('RpnSymbol expected but got %s' % type(val))
60
+        super().__setitem(idx, val)
61
+
62
+    @classmethod
63
+    def random(cls, sz):
64
+        return cls([RpnSymbol.random() for _ in range(sz)])
65
+
66
+    @classmethod
67
+    def from_string(cls, val):
68
+        ''' @brief generate an expr from a string 
69
+            @param val str : expression
70
+            @return RpnExpr
71
+        '''
72
+        return cls([RpnSymbol.from_string(s.strip())
73
+                    for s in val.split()
74
+                    if len(s.strip()) > 0])
75
+
44 76
 class RpnSymbol(object):
45 77
     ''' @brief Designed to handle operation and operand for Turmit expr '''
46 78
 
@@ -56,10 +88,29 @@ class RpnSymbol(object):
56 88
         '''
57 89
         self.optype = optype
58 90
         self.value = value
91
+
92
+        err = False
93
+        err_type = 'Unknown'
59 94
         if optype == self.OPERATION:
60
-            self.value = list(_op_list.keys())[value % len(_op_list)]
95
+            if isinstance(value, str):
96
+                if not value in _op_list:
97
+                    err = True
98
+                    err_type = 'operation'
99
+                else:
100
+                    self.value = value
101
+            else:
102
+                self.value = list(_op_list.keys())[value % len(_op_list)]
61 103
         elif optype == self.VARIABLE:
62
-            self.value = list(_var_list.keys())[value % len(_var_list)]
104
+            if isinstance(value, str):
105
+                if not value in _var_list:
106
+                    err = True
107
+                    err_type = 'variable'
108
+                else:
109
+                    self.value = value
110
+            else:
111
+                self.value = list(_var_list.keys())[value % len(_var_list)]
112
+        if err:
113
+            msg = 'Invalid %s : "%s"' % (err_type, value.upper())
63 114
 
64 115
     def __str__(self):
65 116
         ''' @brief Return a string representation of current symbol '''
@@ -70,6 +121,31 @@ class RpnSymbol(object):
70 121
         else:
71 122
             return self.value.upper()
72 123
 
124
+    def __repr__(self):
125
+        if self.optype == self.OPERATION:
126
+            optype = 'OPE'
127
+        elif self.optype == self.VALUE:
128
+            optype = 'VAL'
129
+        elif self.optype == self.VARIABLE:
130
+            optype = 'VAR'
131
+        name = '%s.%s' % (self.__class__.__module__,
132
+                        self.__class__.__name__)
133
+        return '<%s %s(%s)>' % (name, optype, self.value)
134
+
135
+    @classmethod
136
+    def from_string(cls, val):
137
+        try:
138
+            val = int(val, base=0)
139
+            return cls(val, cls.VALUE)
140
+        except Exception:
141
+            pass
142
+        if val.lower() in _op_list.keys():
143
+            return cls(val, cls.OPERATION)
144
+        elif val.lower() in _var_list.keys():
145
+            return cls(val, cls.VARIABLE)
146
+        raise ValueError('Unrecognized symbol "%s"' % val)
147
+            
148
+
73 149
     @classmethod
74 150
     def random(cls, optype=None):
75 151
         ''' @brief Return a randomly generated symbol '''

+ 64
- 2
gte/turmit.py Ver fichero

@@ -5,16 +5,75 @@ class Turmit(object):
5 5
         infinite looping stack with variable stack size.
6 6
     '''
7 7
     
8
-    def __init__(self, stack_size=8):
8
+    def __init__(self, stack_size=8, **kwargs):
9
+        ''' @brief Instanciated a new Turmit with a programm and a memory stack
10
+            @param stack_size int : stack_size
11
+            @param prog str | RpnExpr | None : turmit programm if None generate
12
+            it randomlt
13
+            @param prog_size int : if prog is generated randomly it will make
14
+            this size
15
+            @param max_int int : maximum integer value + 1
16
+            @param signed_int bool : if True integers are signed
17
+	    @throw ValueError for bad option value
18
+	    @throw RuntimError for bad arguments
19
+        '''
20
+        if stack_size < 2:
21
+            msg = 'Stack size has to be >= 2 but %d given' % (stack_size)
22
+            raise ValueError(msg)
23
+
9 24
         ## @brief List that represent a stack
10 25
         self._stack = [ 0 for _ in range(stack_size)]
11 26
         ## @brief Stack head index
12 27
         self._cur = stack_size - 1
13 28
         ## @brief Stores turmit direction
14 29
         self._dir = 0
30
+        ## @brief Stores turmit program
31
+        self._prog = None
32
+        self._prog_sz = 42
33
+
34
+        ## @brief Stores maximum int value + 1
35
+        #  @note This limit is handled by @ref rpnlib.RpnOp()
36
+        self._max_int = 0x10000
37
+        ## @brief If True integers are signed
38
+        #  @note This limit is handled by @ref rpnlib.RpnOp()
39
+        self._signed_int = True
40
+
41
+        if 'max_int' in kwargs:
42
+            v = kwargs['max_int']
43
+            if v < 1:
44
+                raise ValueError('Maximum int have to be >= 1 but %d found' % v)
45
+            self._max_int = kwargs['max_int']
46
+            del(kwargs['max_int'])
47
+        if 'signed_int' in kwargs:
48
+            self._signed_int = bool(kwargs['signed_int'])
49
+            del(kwargs['signed_int'])
50
+        if 'prog_size' in kwargs:
51
+            v = kwargs['prog_size']
52
+            if v < 2:
53
+                raise ValueError('Prog_size have to be >= 2 but %d found' % v)
54
+            self._prog_sz = v
55
+            del(kwargs['prog_size'])
56
+        if 'prog' in kwargs:
57
+            v = kwargs['prog']
58
+            if isinstance(v, str):
59
+                v = RpnExpr.from_string(v)
60
+            if isinstance(v, RpnExpr):
61
+                self._prog = v
62
+            else:
63
+                msg = 'Str or RpnExpr expected but %s found' % (type(v))
64
+                raise TypeError(msg)
65
+            del(kwargs['prog'])
66
+        if len(kwargs) > 0:
67
+            msg = 'Unexpected arguments : [%s]' % ', '.join(kwargs.keys())
68
+            raise RuntimeError(msg)
69
+
70
+        if self._prog is None:
71
+            self._prog = RpnExpr.random(self._prog_sz)
72
+            
15 73
 
16 74
     @property
17 75
     def shead(self):
76
+        ''' @brief Stack head attribute '''
18 77
         return self._stack[self._cur]
19 78
 
20 79
     def _pop(self):
@@ -46,8 +105,11 @@ class Turmit(object):
46 105
 
47 106
     @RpnOp
48 107
     def mem_sz(self, new_sz):
108
+        ''' @brief Update memory stack size
109
+        '''
110
+	    
49 111
         if new_sz < 2:
50
-            raise ValueError('New size sould be >= 2')
112
+            new_sz = 2
51 113
         stksz = len(self._stack)
52 114
         if new_sz > stksz:
53 115
             self._stack += [ 0 for _ in range(new_sz - stksz) ]

+ 27
- 0
tests/test_rpnexpr.py Ver fichero

@@ -0,0 +1,27 @@
1
+import unittest
2
+
3
+from gte.turmit import Turmit
4
+from gte.rpnlib import _op_list, RpnSymbol, RpnExpr
5
+
6
+class RpnExprTestCase(unittest.TestCase):
7
+
8
+    def test_init(self):
9
+        expr = RpnExpr()
10
+
11
+    def test_random(self):
12
+        expr = RpnExpr.random(5)
13
+        self.assertEqual(len(expr), 5)
14
+
15
+    def test_fromstring(self):
16
+        expr = RpnExpr.from_string("""    5   0xFF
17
+        X
18
+        \tMOD""")
19
+        self.assertEqual(len(expr), 4)
20
+        tests = ((RpnSymbol.VALUE, 5),
21
+                 (RpnSymbol.VALUE, 255),
22
+                 (RpnSymbol.VARIABLE, 'X'),
23
+                 (RpnSymbol.OPERATION, 'MOD'))
24
+        for i, (optype, val) in enumerate(tests):
25
+            self.assertEqual(expr[i].optype, optype)
26
+            self.assertEqual(expr[i].value, val)
27
+

+ 4
- 0
tests/test_rpnsymbol.py Ver fichero

@@ -14,3 +14,7 @@ class RpnSymbolTestCase(unittest.TestCase):
14 14
     def test_value(self):
15 15
         sym = RpnSymbol(42, RpnSymbol.VALUE)
16 16
         self.assertEqual(str(sym), '0x002A')
17
+
18
+    def test_op(self):
19
+        sym = RpnSymbol(0, RpnSymbol.OPERATION)
20
+        self.assertEqual(str(sym), 'MEM_SZ')

+ 30
- 1
tests/test_turmit.py Ver fichero

@@ -6,13 +6,42 @@ from gte.rpnlib import _op_list
6 6
 
7 7
 class TurmitTestCase(unittest.TestCase):
8 8
     
9
-    def test_init_class(self):
9
+    def test_init(self):
10 10
         ''' Test Turmit class __init__ '''
11 11
         t = Turmit(42)
12 12
         self.assertEqual(len(t._stack), 42)
13 13
         self.assertEqual(t.shead, 0)
14 14
         self.assertEqual(len(t._stack) - 1 , t._cur)
15 15
 
16
+    def test_init_args(self):
17
+        ''' Test Turmit __init__ arguments '''
18
+        t = Turmit()
19
+        t = Turmit(prog_size = 42)
20
+        t = Turmit(prog = 'G R B 44 MOD ADD 43 MOD ADD 42 MOD')
21
+        t = Turmit(prog_size = 10)
22
+        self.assertEqual(len(t._prog), 10)
23
+        t = Turmit(max_int = 42)
24
+        self.assertEqual(t._max_int, 42)
25
+        t = Turmit(signed_int = False)
26
+        self.assertFalse(t._signed_int)
27
+        t = Turmit(prog = 'G R B ADD ADD 42 MOD', max_int = 42,
28
+                   signed_int = False, stack_size=42)
29
+
30
+    def test_init_badargs(self):
31
+        ''' Test Turmit __init__ bad arguments '''
32
+        with self.assertRaises(RuntimeError):
33
+            t = Turmit(foobar=42)
34
+        with self.assertRaises(ValueError):
35
+            t = Turmit(-1)
36
+        with self.assertRaises(ValueError):
37
+            t = Turmit(max_int = -1)
38
+        with self.assertRaises(ValueError):
39
+            t = Turmit(prog='foobar')
40
+
41
+
42
+
43
+class TurmitOperationTestCase(unittest.TestCase):
44
+    
16 45
     def test_push(self):
17 46
         ''' Test Turmit _push() method '''
18 47
         t = Turmit()

Loading…
Cancelar
Guardar