tree_ir.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. class Instruction(object):
  2. """A base class for instructions. An instruction is essentially a syntax
  3. node that must first be defined, and can only then be used."""
  4. def __init__(self):
  5. pass
  6. def has_result(self):
  7. """Tells if this instruction computes a result."""
  8. return True
  9. def has_definition(self):
  10. """Tells if this instruction requires a definition."""
  11. return True
  12. def generate_python_def(self, code_generator):
  13. """Generates a Python statement that executes this instruction.
  14. The statement is appended immediately to the code generator."""
  15. if self.has_definition():
  16. raise NotImplementedError()
  17. else:
  18. code_generator.append_line('pass')
  19. def generate_python_use(self, code_generator):
  20. """Generates a Python expression that retrieves this instruction's
  21. result. The expression is returned as a string."""
  22. if self.has_result():
  23. return code_generator.get_result_name(self)
  24. else:
  25. return 'None'
  26. def simplify(self):
  27. """Applies basic simplification to this instruction and its children."""
  28. return self
  29. def __str__(self):
  30. code_generator = PythonGenerator()
  31. self.generate_python_def(code_generator)
  32. return code_generator.code
  33. class PythonGenerator(object):
  34. """Generates Python code from instructions."""
  35. def __init__(self):
  36. self.code = ''
  37. self.indentation_string = ' ' * 4
  38. self.indentation = 0
  39. self.result_value_dict = {}
  40. def append(self, text):
  41. """Appends the given string to this code generator."""
  42. self.code += text
  43. def append_indentation(self):
  44. """Appends indentation to the code generator."""
  45. self.append(self.indentation_string * self.indentation)
  46. def append_line(self, line=None):
  47. """Appends the indentation string followed by the given string (if any)
  48. and a newline to the code generator."""
  49. self.append_indentation()
  50. if line is not None:
  51. self.append(line)
  52. self.append('\n')
  53. def increase_indentation(self):
  54. """Increases the code generator's indentation by one indent."""
  55. self.indentation += 1
  56. def decrease_indentation(self):
  57. """Decreases the code generator's indentation by one indent."""
  58. self.indentation -= 1
  59. def get_result_name(self, instruction):
  60. """Gets the name of the given instruction's result variable."""
  61. if instruction not in self.result_value_dict:
  62. self.result_value_dict[instruction] = \
  63. 'tmp' + str(len(self.result_value_dict))
  64. return self.result_value_dict[instruction]
  65. def append_definition(self, lhs, rhs):
  66. """Defines the first instruction's result variable as the second
  67. instruction's result."""
  68. self.append_line(
  69. self.get_result_name(lhs) + ' = ' + rhs.generate_python_use(self))
  70. def append_state_definition(self, lhs, opcode, args):
  71. """Appends a definition that queries the modelverse state."""
  72. self.append_line(
  73. "%s, = yield [('%s', [%s])]" % (
  74. self.get_result_name(lhs),
  75. opcode,
  76. ', '.join([arg_i.generate_python_use(self) for arg_i in args])))
  77. class VoidInstruction(Instruction):
  78. """A base class for instructions that do not return a value."""
  79. def has_result(self):
  80. """Tells if this instruction computes a result."""
  81. return False
  82. class EmptyInstruction(VoidInstruction):
  83. """Represents the empty instruction, which does nothing."""
  84. def has_definition(self):
  85. """Tells if this instruction requires a definition."""
  86. return False
  87. class SelectInstruction(Instruction):
  88. """Represents a select-instruction: an instruction that defines one of two
  89. child instructions, and sets its result to the defined child's result."""
  90. def __init__(self, condition, if_clause, else_clause):
  91. Instruction.__init__(self)
  92. self.condition = condition
  93. self.if_clause = if_clause
  94. self.else_clause = else_clause
  95. def has_result(self):
  96. """Tells if this instruction computes a result."""
  97. return self.if_clause.has_result() or self.else_clause.has_result()
  98. def simplify(self):
  99. """Applies basic simplification to this instruction and its children."""
  100. simple_cond = self.condition.simplify()
  101. simple_if = self.if_clause.simplify()
  102. simple_else = self.else_clause.simplify()
  103. if isinstance(simple_cond, LiteralInstruction):
  104. return simple_if if simple_cond.literal else simple_else
  105. else:
  106. return SelectInstruction(simple_cond, simple_if, simple_else)
  107. def generate_python_def(self, code_generator):
  108. """Generates Python code for this instruction."""
  109. if_has_result = self.has_result()
  110. if self.condition.has_definition():
  111. self.condition.generate_python_def(code_generator)
  112. code_generator.append_line(
  113. 'if ' + self.condition.generate_python_use(code_generator) + ':')
  114. code_generator.increase_indentation()
  115. self.if_clause.generate_python_def(code_generator)
  116. if if_has_result:
  117. code_generator.append_definition(self, self.if_clause)
  118. code_generator.decrease_indentation()
  119. if self.else_clause.has_definition() or if_has_result:
  120. code_generator.append_line('else:')
  121. code_generator.increase_indentation()
  122. self.else_clause.generate_python_def(code_generator)
  123. if if_has_result:
  124. code_generator.append_definition(self, self.else_clause)
  125. code_generator.decrease_indentation()
  126. class ReturnInstruction(VoidInstruction):
  127. """Represents a return-instruction."""
  128. def __init__(self, value):
  129. VoidInstruction.__init__(self)
  130. self.value = value
  131. def simplify(self):
  132. """Applies basic simplification to this instruction and its children."""
  133. return LoopInstruction(self.value.simplify())
  134. def generate_python_def(self, code_generator):
  135. """Generates Python code for this instruction."""
  136. code_generator.append_line(
  137. 'raise PrimitiveFinished(' +
  138. self.value.generate_python_use(code_generator) +
  139. ')')
  140. class LoopInstruction(VoidInstruction):
  141. """Represents a loop-instruction, which loops until broken."""
  142. def __init__(self, body):
  143. VoidInstruction.__init__(self)
  144. self.body = body
  145. def simplify(self):
  146. """Applies basic simplification to this instruction and its children."""
  147. return LoopInstruction(self.body.simplify())
  148. def generate_python_def(self, code_generator):
  149. """Generates Python code for this instruction."""
  150. code_generator.append_line('while True:')
  151. code_generator.increase_indentation()
  152. self.body.generate_python_def(code_generator)
  153. code_generator.decrease_indentation()
  154. class BreakInstruction(VoidInstruction):
  155. """Represents a break-instruction."""
  156. def generate_python_def(self, code_generator):
  157. """Generates Python code for this instruction."""
  158. code_generator.append_line('break')
  159. class ContinueInstruction(VoidInstruction):
  160. """Represents a continue-instruction."""
  161. def generate_python_def(self, code_generator):
  162. """Generates Python code for this instruction."""
  163. code_generator.append_line('continue')
  164. class CompoundInstruction(Instruction):
  165. """Represents an instruction that evaluates two other instructions
  166. in order, and returns the second instruction's result."""
  167. def __init__(self, first, second):
  168. Instruction.__init__(self)
  169. self.first = first
  170. self.second = second
  171. def has_result(self):
  172. """Tells if this instruction has a result."""
  173. return self.second.has_result()
  174. def simplify(self):
  175. """Applies basic simplification to this instruction and its children."""
  176. simple_fst, simple_snd = self.first.simplify(), self.second.simplify()
  177. if not simple_fst.has_definition():
  178. return simple_snd
  179. elif (not simple_snd.has_definition()) and (not simple_snd.has_result()):
  180. return simple_fst
  181. else:
  182. return CompoundInstruction(simple_fst, simple_snd)
  183. def generate_python_def(self, code_generator):
  184. """Generates Python code for this instruction."""
  185. self.first.generate_python_def(code_generator)
  186. self.second.generate_python_def(code_generator)
  187. if self.has_result():
  188. code_generator.append_definition(self, self.second)
  189. class LiteralInstruction(Instruction):
  190. """Represents an integer, floating-point, string or Boolean literal."""
  191. def __init__(self, literal):
  192. Instruction.__init__(self)
  193. self.literal = literal
  194. def has_definition(self):
  195. """Tells if this instruction requires a definition."""
  196. return False
  197. def generate_python_use(self, code_generator):
  198. """Generates a Python expression that retrieves this instruction's
  199. result. The expression is returned as a string."""
  200. return repr(self.literal)
  201. class StateInstruction(Instruction):
  202. """An instruction that accesses the modelverse state."""
  203. def get_opcode(self):
  204. """Gets the opcode for this state instruction."""
  205. raise NotImplementedError()
  206. def get_arguments(self):
  207. """Gets this state instruction's argument list."""
  208. raise NotImplementedError()
  209. def generate_python_def(self, code_generator):
  210. """Generates a Python statement that executes this instruction.
  211. The statement is appended immediately to the code generator."""
  212. args = self.get_arguments()
  213. for arg_i in args:
  214. if arg_i.has_definition():
  215. arg_i.generate_python_def(code_generator)
  216. code_generator.append_state_definition(self, self.get_opcode(), args)
  217. class LocalInstruction(Instruction):
  218. """A base class for instructions that access local variables."""
  219. def __init__(self, name):
  220. Instruction.__init__(self)
  221. self.name = name
  222. def create_load(self):
  223. """Creates an instruction that loads the variable referenced by this instruction."""
  224. return LoadLocalInstruction(self.name)
  225. def generate_python_use(self, code_generator):
  226. """Generates a Python expression that retrieves this instruction's
  227. result. The expression is returned as a string."""
  228. return self.name
  229. class StoreLocalInstruction(LocalInstruction):
  230. """An instruction that stores a value in a local variable."""
  231. def __init__(self, name, value):
  232. LocalInstruction.__init__(self, name)
  233. self.value = value
  234. def simplify(self):
  235. """Applies basic simplification to this instruction and its children."""
  236. return StoreLocalInstruction(self.name, self.value.simplify())
  237. def generate_python_def(self, code_generator):
  238. """Generates a Python statement that executes this instruction.
  239. The statement is appended immediately to the code generator."""
  240. if self.value.has_definition():
  241. self.value.generate_python_def(code_generator)
  242. code_generator.append_line(
  243. '%s = %s' % (self.name, self.value.generate_python_use(code_generator)))
  244. class LoadLocalInstruction(LocalInstruction):
  245. """An instruction that loads a value from a local variable."""
  246. def has_definition(self):
  247. """Tells if this instruction requires a definition."""
  248. return False
  249. class LoadIndexInstruction(Instruction):
  250. """An instruction that produces a value by indexing a specified expression with
  251. a given key."""
  252. def __init__(self, indexed, key):
  253. Instruction.__init__(self)
  254. self.indexed = indexed
  255. self.key = key
  256. def has_definition(self):
  257. """Tells if this instruction requires a definition."""
  258. return False
  259. def generate_python_use(self, code_generator):
  260. """Generates a Python expression that retrieves this instruction's
  261. result. The expression is returned as a string."""
  262. if self.indexed.has_definition():
  263. self.indexed.generate_python_def(code_generator)
  264. if self.key.has_definition():
  265. self.key.generate_python_def(code_generator)
  266. return "%s[%s]" % (
  267. self.indexed.generate_python_use(code_generator),
  268. self.key.generate_python_use(code_generator))
  269. class ReadValueInstruction(StateInstruction):
  270. """An instruction that reads a value from a node."""
  271. def __init__(self, node_id):
  272. StateInstruction.__init__(self)
  273. self.node_id = node_id
  274. def simplify(self):
  275. """Applies basic simplification to this instruction and its children."""
  276. return ReadValueInstruction(self.node_id.simplify())
  277. def get_opcode(self):
  278. """Gets the opcode for this state instruction."""
  279. return "RV"
  280. def get_arguments(self):
  281. """Gets this state instruction's argument list."""
  282. return [self.node_id]
  283. class ReadDictionaryValueInstruction(StateInstruction):
  284. """An instruction that reads a dictionary value."""
  285. def __init__(self, node_id, key):
  286. StateInstruction.__init__(self)
  287. self.node_id = node_id
  288. self.key = key
  289. def simplify(self):
  290. """Applies basic simplification to this instruction and its children."""
  291. return ReadDictionaryValueInstruction(
  292. self.node_id.simplify(),
  293. self.key.simplify())
  294. def get_opcode(self):
  295. """Gets the opcode for this state instruction."""
  296. return "RD"
  297. def get_arguments(self):
  298. """Gets this state instruction's argument list."""
  299. return [self.node_id, self.key]
  300. class ReadDictionaryEdgeInstruction(StateInstruction):
  301. """An instruction that reads a dictionary edge."""
  302. def __init__(self, node_id, key):
  303. StateInstruction.__init__(self)
  304. self.node_id = node_id
  305. self.key = key
  306. def simplify(self):
  307. """Applies basic simplification to this instruction and its children."""
  308. return ReadDictionaryEdgeInstruction(
  309. self.node_id.simplify(),
  310. self.key.simplify())
  311. def get_opcode(self):
  312. """Gets the opcode for this state instruction."""
  313. return "RDE"
  314. def get_arguments(self):
  315. """Gets this state instruction's argument list."""
  316. return [self.node_id, self.key]
  317. class CreateNodeInstruction(StateInstruction):
  318. """An instruction that creates an empty node."""
  319. def get_opcode(self):
  320. """Gets the opcode for this state instruction."""
  321. return "CN"
  322. def get_arguments(self):
  323. """Gets this state instruction's argument list."""
  324. return []
  325. class CreateDictionaryEdgeInstruction(StateInstruction):
  326. """An instruction that creates a dictionary edge."""
  327. def __init__(self, source_id, key, target_id):
  328. StateInstruction.__init__(self)
  329. self.source_id = source_id
  330. self.key = key
  331. self.target_id = target_id
  332. def simplify(self):
  333. """Applies basic simplification to this instruction and its children."""
  334. return CreateDictionaryEdgeInstruction(
  335. self.source_id.simplify(),
  336. self.key.simplify(),
  337. self.target_id.simplify())
  338. def get_opcode(self):
  339. """Gets the opcode for this state instruction."""
  340. return "CD"
  341. def get_arguments(self):
  342. """Gets this state instruction's argument list."""
  343. return [self.source_id, self.key, self.target_id]
  344. class DeleteEdgeInstruction(StateInstruction):
  345. """An instruction that deletes an edge."""
  346. def __init__(self, edge_id):
  347. StateInstruction.__init__(self)
  348. self.edge_id = edge_id
  349. def simplify(self):
  350. """Applies basic simplification to this instruction and its children."""
  351. return DeleteEdgeInstruction(self.edge_id.simplify())
  352. def has_result(self):
  353. """Tells if this instruction computes a result."""
  354. return False
  355. def get_opcode(self):
  356. """Gets the opcode for this state instruction."""
  357. return "DE"
  358. def get_arguments(self):
  359. """Gets this state instruction's argument list."""
  360. return [self.edge_id]
  361. def create_block(*statements):
  362. """Creates a block-statement from the given list of statements."""
  363. length = len(statements)
  364. if length == 0:
  365. return EmptyInstruction()
  366. elif length == 1:
  367. return statements[0]
  368. else:
  369. return CompoundInstruction(
  370. statements[0],
  371. create_block(*statements[1:]))
  372. if __name__ == "__main__":
  373. example_tree = SelectInstruction(
  374. LiteralInstruction(True),
  375. LoopInstruction(
  376. CompoundInstruction(
  377. BreakInstruction(),
  378. CompoundInstruction(
  379. EmptyInstruction(),
  380. ContinueInstruction()
  381. )
  382. )
  383. ),
  384. ReturnInstruction(
  385. EmptyInstruction()))
  386. print(example_tree.simplify())