main.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. from modelverse_state import status
  2. import sys
  3. from collections import defaultdict
  4. import os
  5. import cPickle as pickle
  6. # Work around Python 2 where a 'big integer' automatically becomes a long
  7. if sys.version > '3': # pragma: no cover
  8. integer_types = (int,)
  9. primitive_types = (int, float, str, bool)
  10. else: # pragma: no cover
  11. integer_types = (int, long)
  12. primitive_types = (int, long, float, str, bool, unicode)
  13. complex_primitives = frozenset(["if", "while", "assign", "call", "break", "continue", "return","resolve","access", "constant", "input", "output", "declare", "global"])
  14. def instance_to_string(value):
  15. return value["value"]
  16. def string_to_instance(value):
  17. return {'value': value}
  18. class ModelverseState(object):
  19. def __init__(self, bootfile = None):
  20. self.free_id = 0
  21. self.edges = {}
  22. self.outgoing = defaultdict(set)
  23. self.incoming = defaultdict(set)
  24. self.values = {}
  25. self.nodes = set()
  26. self.cache = {}
  27. if bootfile is not None:
  28. self.root = self.parse(bootfile)
  29. else:
  30. self.root, _ = self.create_node()
  31. def dump_modelverse(self):
  32. with open("/tmp/modelverse.out", "w") as f:
  33. f.write("digraph main {\n")
  34. for n in self.nodes:
  35. if n in self.values:
  36. f.write("a_%s [label=\"a_%s (%s)\"];\n" % (n, n, self.values[n]))
  37. else:
  38. f.write("a_%s [label=\"a_%s\"];\n" % (n, n))
  39. for i, e in self.edges.iteritems():
  40. f.write("%s -> %s [label=\"%s\"];\n" % (e[0], e[1], i))
  41. f.write("}")
  42. return (self.root, status.SUCCESS)
  43. def parse(self, filename):
  44. picklefile = filename + ".pickle"
  45. try:
  46. if os.path.getmtime(picklefile) > os.path.getmtime(filename):
  47. # Pickle is more recent than grammarfile, so we can use it
  48. self.root, self.free_id, self.nodes, self.edges, self.values = pickle.load(open(picklefile, 'rb'))
  49. for name in self.edges:
  50. source, destination = self.edges[name]
  51. self.outgoing[source].add(name)
  52. self.incoming[destination].add(name)
  53. return self.root
  54. else:
  55. raise Exception("Invalid pickle")
  56. except Exception as e:
  57. # We have to parse the file and create the pickle
  58. symbols = {}
  59. def resolve(symb):
  60. try:
  61. return int(symb)
  62. except:
  63. if symb[0] == "?":
  64. derefs = symb[1:].split("/")
  65. v, _ = self.read_dict(symbols["root"], "__hierarchy")
  66. for deref in derefs:
  67. v, _ = self.read_dict(v, deref)
  68. return v
  69. else:
  70. return symbols[symb]
  71. with open(filename, 'r') as f:
  72. for line in f:
  73. element_type, constructor = line.split(None, 1)
  74. name, values = constructor.split("(", 1)
  75. name = name.split()[0]
  76. values, _ = values.rsplit(")", 1)
  77. if element_type == "Node":
  78. if values == "":
  79. symbols[name], status = self.create_node()
  80. else:
  81. value = values
  82. if value in complex_primitives:
  83. value = string_to_instance(value)
  84. else:
  85. value = eval(value)
  86. symbols[name], status = self.create_nodevalue(value)
  87. elif element_type == "Edge":
  88. values = [v.split()[0] for v in values.split(",")]
  89. symbols[name], status = self.create_edge(resolve(values[0]), resolve(values[1]))
  90. else:
  91. raise Exception("Unknown element type: %s" % element_type)
  92. if status != 100:
  93. raise Exception("Failed to process line for reason %s: %s" % (status, line))
  94. # Creation successful, now also create a pickle
  95. with open(picklefile, 'wb') as f:
  96. pickle.dump((symbols["root"], self.free_id, self.nodes, self.edges, self.values), f, pickle.HIGHEST_PROTOCOL)
  97. return symbols["root"]
  98. def read_root(self):
  99. return (self.root, status.SUCCESS)
  100. def create_node(self):
  101. self.nodes.add(self.free_id)
  102. self.free_id += 1
  103. return (self.free_id - 1, status.SUCCESS)
  104. def create_edge(self, source, target):
  105. if source not in self.edges and source not in self.nodes:
  106. return (None, status.FAIL_CE_SOURCE)
  107. elif target not in self.edges and target not in self.nodes:
  108. return (None, status.FAIL_CE_TARGET)
  109. else:
  110. self.outgoing[source].add(self.free_id)
  111. self.incoming[target].add(self.free_id)
  112. self.edges[self.free_id] = (source, target)
  113. self.free_id += 1
  114. return (self.free_id - 1, status.SUCCESS)
  115. def is_valid_datavalue(self, value):
  116. if isinstance(value, dict):
  117. if "value" in value and value["value"] in complex_primitives:
  118. return True
  119. else:
  120. return False
  121. elif not isinstance(value, primitive_types):
  122. return False
  123. return True
  124. def create_nodevalue(self, value):
  125. if not self.is_valid_datavalue(value):
  126. print("Not correct: " + str(value))
  127. return (None, status.FAIL_CNV_OOB)
  128. self.values[self.free_id] = value
  129. self.nodes.add(self.free_id)
  130. self.free_id += 1
  131. return (self.free_id - 1, status.SUCCESS)
  132. def create_dict(self, source, data, destination):
  133. if source not in self.nodes and source not in self.edges:
  134. return (None, status.FAIL_CDICT_SOURCE)
  135. if destination not in self.nodes and destination not in self.edges:
  136. return (None, status.FAIL_CDICT_TARGET)
  137. if not self.is_valid_datavalue(data):
  138. return (None, status.FAIL_CDICT_OOB)
  139. n = self.create_nodevalue(data)[0]
  140. e = self.create_edge(source, destination)[0]
  141. self.create_edge(e, n)
  142. self.cache.setdefault(source, {})[data] = e
  143. return (None, status.SUCCESS)
  144. def read_value(self, node):
  145. if node not in self.nodes:
  146. return (None, status.FAIL_RV_UNKNOWN)
  147. v = self.values.get(node, None)
  148. if v is None:
  149. return (None, status.FAIL_RV_NO_VALUE)
  150. else:
  151. return (v, status.SUCCESS)
  152. def read_outgoing(self, elem):
  153. if elem in self.edges or elem in self.nodes:
  154. return (list(self.outgoing[elem]), status.SUCCESS)
  155. else:
  156. return (None, status.FAIL_RO_UNKNOWN)
  157. def read_incoming(self, elem):
  158. if elem in self.edges or elem in self.nodes:
  159. return (list(self.incoming[elem]), status.SUCCESS)
  160. else:
  161. return (None, status.FAIL_RI_UNKNOWN)
  162. def read_edge(self, edge):
  163. v = self.edges.get(edge, None)
  164. if v is None:
  165. return ([None, None], status.FAIL_RE_UNKNOWN)
  166. else:
  167. s, t = v
  168. return ([s, t], status.SUCCESS)
  169. def read_dict(self, node, value):
  170. e, s = self.read_dict_edge(node, value)
  171. if s != status.SUCCESS:
  172. return (None, {status.FAIL_RDICTE_UNKNOWN: status.FAIL_RDICT_UNKNOWN,
  173. status.FAIL_RDICTE_UNCERTAIN: status.FAIL_RDICT_UNCERTAIN,
  174. status.FAIL_RDICTE_OOB: status.FAIL_RDICT_OOB,
  175. status.FAIL_RDICTE_NOT_FOUND: status.FAIL_RDICT_NOT_FOUND,
  176. status.FAIL_RDICTE_AMBIGUOUS: status.FAIL_RDICT_AMBIGUOUS}[s])
  177. return (self.edges[e][1], status.SUCCESS)
  178. def read_dict_keys(self, node):
  179. if node not in self.nodes and node not in self.edges:
  180. return (None, status.FAIL_RDICTKEYS_UNKNOWN)
  181. result = []
  182. for e1 in self.outgoing.get(node, set()):
  183. data_links = self.outgoing.get(e1, set())
  184. for e2 in data_links:
  185. result.append(self.edges[e2][1])
  186. return (result, status.SUCCESS)
  187. def read_dict_edge(self, node, value):
  188. try:
  189. first = self.cache[node][value]
  190. # Got hit, so validate
  191. if (self.edges[first][0] == node) and \
  192. (len(self.outgoing[first]) == 1) and \
  193. (self.values[self.edges[list(self.outgoing[first])[0]][1]] == value):
  194. return (first, status.SUCCESS)
  195. del self.cache[node][value]
  196. except KeyError:
  197. # Didn't exist
  198. pass
  199. if node not in self.nodes and node not in self.edges:
  200. return (None, status.FAIL_RDICTE_UNKNOWN)
  201. if not self.is_valid_datavalue(value):
  202. return (None, status.FAIL_RDICTE_OOB)
  203. # Get all outgoing links
  204. found = None
  205. for e1 in self.outgoing.get(node, set()):
  206. data_links = self.outgoing.get(e1, set())
  207. # For each link, we read the links that might link to a data value
  208. for e2 in data_links:
  209. # Now read out the target of the link
  210. target = self.edges[e2][1]
  211. # And access its value
  212. v = self.values.get(target, None)
  213. if v == value:
  214. # Found a match
  215. # Now get the target of the original link
  216. if len(data_links) > 1:
  217. return (None, status.FAIL_RDICTE_UNCERTAIN)
  218. else:
  219. if found is not None:
  220. print("Duplicate key on value: %s (%s <-> %s)!" % (v, found, e1))
  221. return (None, status.FAIL_RDICTE_AMBIGUOUS)
  222. found = e1
  223. self.cache.setdefault(node, {})[value] = e1
  224. if found is not None:
  225. return (found, status.SUCCESS)
  226. else:
  227. return (None, status.FAIL_RDICTE_NOT_FOUND)
  228. def read_dict_node(self, node, value_node):
  229. e, s = self.read_dict_node_edge(node, value_node)
  230. if s != status.SUCCESS:
  231. return (None, {status.FAIL_RDICTNE_UNKNOWN: status.FAIL_RDICTN_UNKNOWN,
  232. status.FAIL_RDICTNE_UNCERTAIN: status.FAIL_RDICTN_UNCERTAIN,
  233. status.FAIL_RDICTNE_AMBIGUOUS: status.FAIL_RDICTN_AMBIGUOUS,
  234. status.FAIL_RDICTNE_OOB: status.FAIL_RDICTN_OOB,
  235. status.FAIL_RDICTNE_NOT_FOUND: status.FAIL_RDICTN_NOT_FOUND}[s])
  236. return (self.edges[e][1], status.SUCCESS)
  237. def read_dict_node_edge(self, node, value_node):
  238. if node not in self.nodes and node not in self.edges:
  239. return (None, status.FAIL_RDICTNE_UNKNOWN)
  240. # Get all outgoing links
  241. found = None
  242. for e1 in self.outgoing.get(node, set()):
  243. data_links = self.outgoing.get(e1, set())
  244. # For each link, we read the links that might link to a data value
  245. for e2 in data_links:
  246. # Now read out the target of the link
  247. target = self.edges[e2][1]
  248. # And access its value
  249. if target == value_node:
  250. # Found a match
  251. # Now get the target of the original link
  252. if len(data_links) > 1:
  253. return (None, status.FAIL_RDICTNE_UNCERTAIN)
  254. else:
  255. if found is not None:
  256. print("Duplicate key on node: %s (%s <-> %s)!" % (value_node, found, e1))
  257. return (None, status.FAIL_RDICTNE_AMBIGUOUS)
  258. found = e1
  259. if found is not None:
  260. return (found, status.SUCCESS)
  261. else:
  262. return (None, status.FAIL_RDICTNE_NOT_FOUND)
  263. def read_reverse_dict(self, node, value):
  264. if node not in self.nodes and node not in self.edges:
  265. return (None, status.FAIL_RRDICT_UNKNOWN)
  266. elif not self.is_valid_datavalue(value):
  267. return (None, status.FAIL_RRDICT_OOB)
  268. # Get all outgoing links
  269. matches = []
  270. for e1 in self.incoming.get(node, set()):
  271. data_links = self.outgoing.get(e1, set())
  272. # For each link, we read the links that might link to a data value
  273. for e2 in data_links:
  274. # Now read out the target of the link
  275. target = self.edges[e2][1]
  276. # And access its value
  277. v = self.values.get(target, None)
  278. if v == value:
  279. # Found a match
  280. if len(data_links) > 1:
  281. return (None, status.FAIL_RRDICT_UNCERTAIN)
  282. else:
  283. matches.append(e1)
  284. if len(matches) == 0:
  285. return (None, status.FAIL_RRDICT_NOT_FOUND)
  286. else:
  287. return ([self.edges[e][0] for e in matches], status.SUCCESS)
  288. def delete_node(self, node):
  289. if node not in self.nodes:
  290. return (None, status.FAIL_DN_UNKNOWN)
  291. self.nodes.remove(node)
  292. s = set()
  293. for e in self.outgoing[node]:
  294. s.add(e)
  295. for e in self.incoming[node]:
  296. s.add(e)
  297. for e in s:
  298. self.delete_edge(e)
  299. if node in self.outgoing:
  300. del self.outgoing[node]
  301. if node in self.incoming:
  302. del self.incoming[node]
  303. return (None, status.SUCCESS)
  304. def delete_edge(self, edge):
  305. if edge not in self.edges:
  306. return (None, status.FAIL_DE_UNKNOWN)
  307. s, t = self.edges[edge]
  308. self.incoming[t].remove(edge)
  309. self.outgoing[s].remove(edge)
  310. del self.edges[edge]
  311. s = set()
  312. for e in self.outgoing[edge]:
  313. s.add(e)
  314. for e in self.incoming[edge]:
  315. s.add(e)
  316. for e in s:
  317. self.delete_edge(e)
  318. if edge in self.outgoing:
  319. del self.outgoing[edge]
  320. if edge in self.incoming:
  321. del self.incoming[edge]
  322. return (None, status.SUCCESS)