7 from libraries.recipes import algorithms
18 def parse_number(userInput):
20 base = __BASE_MAPPINGS.get(userInput[0:2], 10)
22 userInput = userInput[2:] # Remove prefix
23 value = int(userInput, base)
29 value = float(userInput)
35 value = complex(userInput)
40 raise ValueError('Cannot parse "%s" as a number' % userInput)
43 class ErrorReporting(object):
45 def push_message(self, message):
46 raise NotImplementedError
48 def push_exception(self, exception):
49 self.push_message(exception.message)
50 warnings.warn(exception, stacklevel=3)
52 def pop_message(self):
53 raise NotImplementedError
56 class ErrorIgnore(ErrorReporting):
58 def push_message(self, message):
61 def pop_message(self):
65 class ErrorWarning(ErrorReporting):
67 def push_message(self, message):
68 warnings.warn(message, stacklevel=2)
70 def pop_message(self):
74 class AbstractHistory(object):
76 Is it just me or is this class name begging for some jokes?
80 raise NotImplementedError
83 raise NotImplementedError
87 for child in node.get_children():
91 raise NotImplementedError
94 raise NotImplementedError
97 raise NotImplementedError
100 raise NotImplementedError
103 class CalcHistory(AbstractHistory):
106 super(CalcHistory, self).__init__()
107 self.__nodeStack = []
109 def push(self, node):
110 assert node is not None
111 self.__nodeStack.append(node)
115 popped = self.__nodeStack[-1]
116 del self.__nodeStack[-1]
120 return self.__nodeStack[-1]
123 self.__nodeStack = []
126 return len(self.__nodeStack)
129 return self.__nodeStack[::-1]
132 class RpnCalcHistory(object):
134 def __init__(self, history, entry, errorReporting, constants, operations):
135 self.history = history
136 self.__entry = weakref.ref(entry)
138 self.__errorReporter = errorReporting
139 self.__constants = constants
140 self.__operations = operations
142 self.__serialRenderer = operation.render_number()
145 def errorReporter(self):
146 return self.__errorReporter
149 def OPERATIONS(self):
150 return self.__operations
154 return self.__constants
158 self.__entry().clear()
160 def push_entry(self):
162 @todo Add operation duplication. If value is empty, peek at the top
163 item. If it has children, grab the last one, push it and reapply the
164 operation. If there are no children then just duplicate the item
167 value = self.__entry().get_value()
171 valueNode = self._parse_value(value)
172 self.history.push(valueNode)
174 self.__entry().clear()
177 def apply_operation(self, Node):
181 node = self._apply_operation(Node)
183 except StandardError, e:
184 self.errorReporter.push_exception(e)
187 def serialize_stack(self):
189 stackNode.serialize(self.__serialRenderer)
190 for stackNode in self.history
192 serialized = list(serialized)
196 def deserialize_stack(self, data):
197 for possibleNode in data:
198 for nodeValue in possibleNode:
199 if nodeValue in self.OPERATIONS:
200 Node = self.OPERATIONS[nodeValue]
201 self._apply_operation(Node)
203 node = self._parse_value(nodeValue)
204 self.history.push(node)
206 def _parse_value(self, userInput):
208 value, base = parse_number(userInput)
209 return operation.Value(value, base)
214 return self.CONSTANTS[userInput]
218 return operation.Variable(userInput)
220 def _apply_operation(self, Node):
221 numArgs = Node.argumentCount
223 if len(self.history) < numArgs:
225 "Not enough arguments. The stack has %d but %s needs %d" % (
226 len(self.history), Node.symbol, numArgs
230 args = [arg for arg in algorithms.func_repeat(numArgs, self.history.pop)]
235 except StandardError:
237 self.history.push(arg)
239 self.history.push(node)