Takes care of managing values produced while vising Python AST nodes. Each visit to a node is expected to match one stack frame. Values produced (meaning pushed) by child frames are accessible (meaning can be popped) by the parent. A frame cannot access the value it produced (it is owne
| 271 | |
| 272 | |
| 273 | class PyStack(object): |
| 274 | """Takes care of managing values produced while vising Python AST nodes. |
| 275 | |
| 276 | Each visit to a node is expected to match one stack frame. Values produced |
| 277 | (meaning pushed) by child frames are accessible (meaning can be popped) by |
| 278 | the parent. A frame cannot access the value it produced (it is owned by the |
| 279 | parent). |
| 280 | """ |
| 281 | |
| 282 | class Frame(object): |
| 283 | |
| 284 | def __init__(self, parent=None): |
| 285 | self.parent = parent |
| 286 | self.entries = None |
| 287 | |
| 288 | def __init__(self, error_handler=None): |
| 289 | |
| 290 | def default_error_handler(msg): |
| 291 | raise RuntimeError(msg) |
| 292 | |
| 293 | self._frame = None |
| 294 | self.emitError = error_handler or default_error_handler |
| 295 | |
| 296 | def pushFrame(self): |
| 297 | """A new frame should be pushed to process a new node in the AST.""" |
| 298 | if self._frame and not self._frame.entries: |
| 299 | self._frame.entries = deque() |
| 300 | self._frame = PyStack.Frame(parent=self._frame) |
| 301 | |
| 302 | def popFrame(self): |
| 303 | """A frame should be popped once a node in the AST has been |
| 304 | processed.""" |
| 305 | if not self._frame: |
| 306 | self.emitError("stack has no frames to pop") |
| 307 | elif self._frame.entries: |
| 308 | self.emitError( |
| 309 | "all values must be processed before popping a frame") |
| 310 | else: |
| 311 | self._frame = self._frame.parent |
| 312 | |
| 313 | def pushValue(self, value): |
| 314 | """Pushes a value to the make it available to the parent frame.""" |
| 315 | if not self._frame: |
| 316 | self.emitError("cannot push value to empty stack") |
| 317 | elif not self._frame.parent: |
| 318 | self.emitError("no parent frame is defined to push values to") |
| 319 | else: |
| 320 | self._frame.parent.entries.append(value) |
| 321 | |
| 322 | def popValue(self): |
| 323 | """Pops the most recently produced (pushed) value by a child frame.""" |
| 324 | if not self._frame: |
| 325 | self.emitError("value stack is empty") |
| 326 | elif not self._frame.entries: |
| 327 | # This is the only error that may be directly user-facing even when |
| 328 | # the bridge is doing its processing correctly. We hence give a |
| 329 | # somewhat general error. For internal purposes, the error might be |
| 330 | # better stated as something like: either this frame has not had a |