summaryrefslogtreecommitdiff
path: root/monkey/util.py
diff options
context:
space:
mode:
authorTimotej Lazar <timotej.lazar@araneo.org>2015-01-15 12:10:22 +0100
committerAleš Smodiš <aless@guru.si>2015-08-11 14:26:01 +0200
commit819ab10281c9bd6c000364c3a243959edd18abf7 (patch)
tree5ca3452418b49781563221bb56cf70e1d0fb1bb8 /monkey/util.py
parentd86793039957aa408a98806aecfb5964bda5fb87 (diff)
Move pymonkey stuff to monkey/
Importing pymonkey into webmonkey, let's see how this works.
Diffstat (limited to 'monkey/util.py')
-rw-r--r--monkey/util.py81
1 files changed, 81 insertions, 0 deletions
diff --git a/monkey/util.py b/monkey/util.py
new file mode 100644
index 0000000..b8be2bb
--- /dev/null
+++ b/monkey/util.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python3
+
+from collections import namedtuple
+from heapq import heappush, heappop
+import itertools
+
+# A simple priority queue based on the heapq class.
+class PQueue(object):
+ REMOVED = '<removed-task>' # placeholder for a removed task
+
+ def __init__(self):
+ self.pq = [] # list of entries arranged in a heap
+ self.entry_finder = {} # mapping of tasks to entries
+ self.counter = itertools.count() # unique sequence count
+ self.size = 0
+
+ def push(self, task, priority=0):
+ 'Add a new task or update the priority of an existing task'
+ if task in self.entry_finder:
+ self.remove(task)
+ else:
+ self.size += 1
+ entry = [priority, next(self.counter), task]
+ self.entry_finder[task] = entry
+ heappush(self.pq, entry)
+
+ def remove(self, task):
+ 'Mark an existing task as REMOVED. Raise KeyError if not found.'
+ entry = self.entry_finder.pop(task)
+ entry[-1] = self.REMOVED
+ self.size -= 1
+
+ def pop(self):
+ 'Remove and return the lowest priority task. Raise KeyError if empty.'
+ while self.pq:
+ priority, count, task = heappop(self.pq)
+ if task is not self.REMOVED:
+ del self.entry_finder[task]
+ self.size -= 1
+ return task
+ return None
+
+ def __len__(self):
+ return self.size
+
+# Stores a token's type and value, and optionally the position of the first
+# character in the lexed stream.
+class Token(namedtuple('Token', ['type', 'val', 'pos'])):
+ __slots__ = ()
+
+ # Custom constructor to support default parameters.
+ def __new__(cls, type, val='', pos=None):
+ return super(Token, cls).__new__(cls, type, val, pos)
+
+ def __str__(self):
+ return self.val
+
+ # Ignore position when comparing tokens. There is probably a cleaner way of
+ # doing these.
+ __eq__ = lambda x, y: x[0] == y[0] and x[1] == y[1]
+ __ne__ = lambda x, y: x[0] != y[0] or x[1] != y[1]
+ __lt__ = lambda x, y: tuple.__lt__(x[0:2], y[0:2])
+ __le__ = lambda x, y: tuple.__le__(x[0:2], y[0:2])
+ __ge__ = lambda x, y: tuple.__ge__(x[0:2], y[0:2])
+ __gt__ = lambda x, y: tuple.__gt__(x[0:2], y[0:2])
+
+ # Only hash token's value (we don't care about position, and types are
+ # determined by values).
+ def __hash__(self):
+ return hash(self[1])
+
+# Return [n]th line in [text].
+def get_line(text, n):
+ lines = text.split('\n')
+ if n >= 0 and n < len(lines):
+ return lines[n]
+ return None
+
+# Indent each line in [text] by [indent] spaces.
+def indent(text, indent=2):
+ return '\n'.join([' '*indent+line for line in text.split('\n')])