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