tree_ir.py 18 KB

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