Reveresed the stack order to be more rpn like
[ejpi] / src / history.py
1 #!/usr/bin/env python
2
3
4 import weakref
5 import warnings
6
7 from libraries.recipes import algorithms
8 import operation
9
10
11 __BASE_MAPPINGS = {
12         "0x": 16,
13         "0o": 8,
14         "0b": 2,
15 }
16
17
18 def parse_number(userInput):
19         try:
20                 base = __BASE_MAPPINGS.get(userInput[0:2], 10)
21                 if base != 10:
22                         userInput = userInput[2:] # Remove prefix
23                 value = int(userInput, base)
24                 return value, base
25         except ValueError:
26                 pass
27
28         try:
29                 value = float(userInput)
30                 return value, 10
31         except ValueError:
32                 pass
33
34         try:
35                 value = complex(userInput)
36                 return value, 10
37         except ValueError:
38                 pass
39
40         raise ValueError('Cannot parse "%s" as a number' % userInput)
41
42
43 class AbstractHistory(object):
44         """
45         Is it just me or is this class name begging for some jokes?
46         """
47
48         def push(self, node):
49                 raise NotImplementedError
50
51         def pop(self):
52                 raise NotImplementedError
53
54         def unpush(self):
55                 node = self.pop()
56                 for child in node.get_children():
57                         self.push(child)
58
59         def peek(self):
60                 raise NotImplementedError
61
62         def clear(self):
63                 raise NotImplementedError
64
65         def __len__(self):
66                 raise NotImplementedError
67
68         def __iter__(self):
69                 raise NotImplementedError
70
71
72 class CalcHistory(AbstractHistory):
73
74         def __init__(self):
75                 super(CalcHistory, self).__init__()
76                 self.__nodeStack = []
77
78         def push(self, node):
79                 assert node is not None
80                 self.__nodeStack.append(node)
81                 return node
82
83         def pop(self):
84                 popped = self.__nodeStack[-1]
85                 del self.__nodeStack[-1]
86                 return popped
87
88         def peek(self):
89                 return self.__nodeStack[-1]
90
91         def clear(self):
92                 self.__nodeStack = []
93
94         def __len__(self):
95                 return len(self.__nodeStack)
96
97         def __iter__(self):
98                 return self.__nodeStack[::-1]
99
100
101 class RpnCalcHistory(object):
102
103         def __init__(self, history, entry, errorReporting, constants, operations):
104                 self.history = history
105                 self.__entry = weakref.ref(entry)
106
107                 self.__errorReporter = errorReporting
108                 self.__constants = constants
109                 self.__operations = operations
110
111                 self.__serialRenderer = operation.render_number()
112
113         @property
114         def errorReporter(self):
115                 return self.__errorReporter
116
117         @property
118         def OPERATIONS(self):
119                 return self.__operations
120
121         @property
122         def CONSTANTS(self):
123                 return self.__constants
124
125         def clear(self):
126                 self.history.clear()
127                 self.__entry().clear()
128
129         def push_entry(self):
130                 """
131                 @todo Add operation duplication.  If value is empty, peek at the top
132                         item.  If it has children, grab the last one, push it and reapply the
133                         operation.  If there are no children then just duplicate the item
134                 """
135
136                 value = self.__entry().get_value()
137
138                 valueNode = None
139                 if 0 < len(value):
140                         valueNode = self._parse_value(value)
141                         self.history.push(valueNode)
142
143                 self.__entry().clear()
144                 return valueNode
145
146         def apply_operation(self, Node):
147                 try:
148                         self.push_entry()
149
150                         node = self._apply_operation(Node)
151                         return node
152                 except StandardError, e:
153                         self.errorReporter.push_exception()
154                         return None
155
156         def serialize_stack(self):
157                 serialized = (
158                         stackNode.serialize(self.__serialRenderer)
159                         for stackNode in self.history
160                 )
161                 serialized = list(serialized)
162                 return serialized
163
164         def deserialize_stack(self, data):
165                 for possibleNode in data:
166                         for nodeValue in possibleNode:
167                                 if nodeValue in self.OPERATIONS:
168                                         Node = self.OPERATIONS[nodeValue]
169                                         self._apply_operation(Node)
170                                 else:
171                                         node = self._parse_value(nodeValue)
172                                         self.history.push(node)
173
174         def _parse_value(self, userInput):
175                 try:
176                         value, base = parse_number(userInput)
177                         return operation.Value(value, base)
178                 except ValueError:
179                         pass
180
181                 try:
182                         return self.CONSTANTS[userInput]
183                 except KeyError:
184                         pass
185
186                 return operation.Variable(userInput)
187
188         def _apply_operation(self, Node):
189                 numArgs = Node.argumentCount
190
191                 if len(self.history) < numArgs:
192                         raise ValueError(
193                                 "Not enough arguments.  The stack has %d but %s needs %d" % (
194                                         len(self.history), Node.symbol, numArgs
195                                 )
196                         )
197
198                 args = [arg for arg in algorithms.func_repeat(numArgs, self.history.pop)]
199                 args.reverse()
200
201                 try:
202                         node = Node(*args)
203                 except StandardError:
204                         for arg in args:
205                                 self.history.push(arg)
206                         raise
207                 self.history.push(node)
208                 return node