tree_ir.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. # NOTE: NOP_LITERAL abuses a mechanic of the modelverse kernel. Specifically,
  2. # whenever the `ModelverseKernel.execute_yields` method returns `None`, then
  3. # the server built around it takes that as a hint that an instruction's phase
  4. # has been completed. The server interrupts the kernel's thread of execution
  5. # when it remarks that an instruction has completed a phase (i.e., when `None`
  6. # is returned by `ModelverseKernel.execute_yields`) and proceeds to check for
  7. # input and output.
  8. #
  9. # In assembly language, a nop is usually used as a point at which a thread of
  10. # execution can be terminated. It follows from the paragraph above that what
  11. # the interpreter does is more or less equivalent to placing nops after every
  12. # instruction. It is worthwhile to remark that JIT-compiled code cannot rely
  13. # on the kernel to interrupt the thread of execution automatically during a
  14. # jitted function's execution -- jitted functions are considered equivalent
  15. # to a single instruction as far as the kernel is concerned. A nop will be
  16. # inserted _after_ the function call (if it is called from interpreted code)
  17. # but that does not suffice for IO, which needs the input/output processing
  18. # to be performed during function execution.
  19. #
  20. # For this reason, the JIT must strategically interrupt the execution of the
  21. # functions it compiles. In other words, it must insert its own nops.
  22. # Here comes the interesting part: a nop is equivalent to `yield None`,
  23. # because that will persuade `ModelverseKernel.execute_yields` to relay the
  24. # `None` marker value to the server, without terminating the current
  25. # generator.
  26. NOP_LITERAL = None
  27. """A literal that results in a nop during which execution may be interrupted
  28. when yielded."""
  29. class Instruction(object):
  30. """A base class for instructions. An instruction is essentially a syntax
  31. node that must first be defined, and can only then be used."""
  32. def __init__(self):
  33. pass
  34. def has_result(self):
  35. """Tells if this instruction computes a result."""
  36. return True
  37. def has_definition(self):
  38. """Tells if this instruction requires a definition."""
  39. return True
  40. def get_result_name_override(self):
  41. """Gets a value that overrides the code generator's result name for this
  42. instruction if it is not None."""
  43. return None
  44. def generate_python_def(self, code_generator):
  45. """Generates a Python statement that executes this instruction.
  46. The statement is appended immediately to the code generator."""
  47. if self.has_definition():
  48. raise NotImplementedError()
  49. else:
  50. code_generator.append_line('pass')
  51. def generate_python_use(self, code_generator):
  52. """Generates a Python expression that retrieves this instruction's
  53. result. The expression is returned as a string."""
  54. if self.has_result():
  55. return code_generator.get_result_name(self)
  56. else:
  57. return 'None'
  58. def simplify(self):
  59. """Applies basic simplification to this instruction and its children."""
  60. return self
  61. def __str__(self):
  62. code_generator = PythonGenerator()
  63. self.generate_python_def(code_generator)
  64. return code_generator.code
  65. class PythonGenerator(object):
  66. """Generates Python code from instructions."""
  67. def __init__(self):
  68. self.code = ''
  69. self.indentation_string = ' ' * 4
  70. self.indentation = 0
  71. self.result_value_dict = {}
  72. def append(self, text):
  73. """Appends the given string to this code generator."""
  74. self.code += text
  75. def append_indentation(self):
  76. """Appends indentation to the code generator."""
  77. self.append(self.indentation_string * self.indentation)
  78. def append_line(self, line=None):
  79. """Appends the indentation string followed by the given string (if any)
  80. and a newline to the code generator."""
  81. self.append_indentation()
  82. if line is not None:
  83. self.append(line)
  84. self.append('\n')
  85. def increase_indentation(self):
  86. """Increases the code generator's indentation by one indent."""
  87. self.indentation += 1
  88. def decrease_indentation(self):
  89. """Decreases the code generator's indentation by one indent."""
  90. self.indentation -= 1
  91. def get_result_name(self, instruction, advised_name=None):
  92. """Gets the name of the given instruction's result variable."""
  93. if instruction not in self.result_value_dict:
  94. override_name = instruction.get_result_name_override()
  95. if override_name is not None:
  96. self.result_value_dict[instruction] = override_name
  97. elif advised_name is not None:
  98. self.result_value_dict[instruction] = advised_name
  99. else:
  100. self.result_value_dict[instruction] = \
  101. 'tmp' + str(len(self.result_value_dict))
  102. return self.result_value_dict[instruction]
  103. def append_definition(self, lhs, rhs):
  104. """Defines the first instruction's result variable as the second
  105. instruction's result."""
  106. self.append_line(
  107. self.get_result_name(lhs) + ' = ' + rhs.generate_python_use(self))
  108. def append_move_definition(self, lhs, rhs):
  109. """First defines the second instruction, then defines the first
  110. instruction as the result of the second."""
  111. if rhs.has_definition():
  112. # Retrieve the result name for the lhs.
  113. lhs_result_name = self.get_result_name(lhs)
  114. # Encourage the rhs to take on the same result name as the lhs.
  115. rhs_result_name = self.get_result_name(rhs, lhs_result_name)
  116. # Generate the rhs' definition.
  117. rhs.generate_python_def(self)
  118. # Only perform an assignment if it's truly necessary.
  119. if lhs_result_name != rhs_result_name:
  120. self.append_definition(lhs, rhs)
  121. else:
  122. self.append_definition(lhs, rhs)
  123. def append_state_definition(self, lhs, opcode, args):
  124. """Appends a definition that queries the modelverse state."""
  125. self.append_line(
  126. "%s, = yield [('%s', [%s])]" % (
  127. self.get_result_name(lhs),
  128. opcode,
  129. ', '.join([arg_i.generate_python_use(self) for arg_i in args])))
  130. class VoidInstruction(Instruction):
  131. """A base class for instructions that do not return a value."""
  132. def has_result(self):
  133. """Tells if this instruction computes a result."""
  134. return False
  135. class EmptyInstruction(VoidInstruction):
  136. """Represents the empty instruction, which does nothing."""
  137. def has_definition(self):
  138. """Tells if this instruction requires a definition."""
  139. return False
  140. class SelectInstruction(Instruction):
  141. """Represents a select-instruction: an instruction that defines one of two
  142. child instructions, and sets its result to the defined child's result."""
  143. def __init__(self, condition, if_clause, else_clause):
  144. Instruction.__init__(self)
  145. self.condition = condition
  146. self.if_clause = if_clause
  147. self.else_clause = else_clause
  148. def has_result(self):
  149. """Tells if this instruction computes a result."""
  150. return self.if_clause.has_result() or self.else_clause.has_result()
  151. def simplify(self):
  152. """Applies basic simplification to this instruction and its children."""
  153. simple_cond = self.condition.simplify()
  154. simple_if = self.if_clause.simplify()
  155. simple_else = self.else_clause.simplify()
  156. if isinstance(simple_cond, LiteralInstruction):
  157. return simple_if if simple_cond.literal else simple_else
  158. else:
  159. return SelectInstruction(simple_cond, simple_if, simple_else)
  160. def generate_python_def(self, code_generator):
  161. """Generates Python code for this instruction."""
  162. if_has_result = self.has_result()
  163. if self.condition.has_definition():
  164. self.condition.generate_python_def(code_generator)
  165. code_generator.append_line(
  166. 'if ' + self.condition.generate_python_use(code_generator) + ':')
  167. code_generator.increase_indentation()
  168. if if_has_result:
  169. code_generator.append_move_definition(self, self.if_clause)
  170. else:
  171. self.if_clause.generate_python_def(code_generator)
  172. code_generator.decrease_indentation()
  173. else_has_def = self.else_clause.has_definition()
  174. if else_has_def or if_has_result:
  175. code_generator.append_line('else:')
  176. code_generator.increase_indentation()
  177. if if_has_result:
  178. code_generator.append_move_definition(self, self.else_clause)
  179. else:
  180. self.else_clause.generate_python_def(code_generator)
  181. code_generator.decrease_indentation()
  182. class ReturnInstruction(VoidInstruction):
  183. """Represents a return-instruction."""
  184. def __init__(self, value):
  185. VoidInstruction.__init__(self)
  186. self.value = value
  187. def simplify(self):
  188. """Applies basic simplification to this instruction and its children."""
  189. return ReturnInstruction(self.value.simplify())
  190. def generate_python_def(self, code_generator):
  191. """Generates Python code for this instruction."""
  192. self.value.generate_python_def(code_generator)
  193. code_generator.append_line(
  194. 'raise PrimitiveFinished(' +
  195. self.value.generate_python_use(code_generator) +
  196. ')')
  197. class RaiseInstruction(VoidInstruction):
  198. """An instruction that raises an error."""
  199. def __init__(self, value):
  200. VoidInstruction.__init__(self)
  201. self.value = value
  202. def simplify(self):
  203. """Applies basic simplification to this instruction and its children."""
  204. return RaiseInstruction(self.value.simplify())
  205. def generate_python_def(self, code_generator):
  206. """Generates Python code for this instruction."""
  207. self.value.generate_python_def(code_generator)
  208. code_generator.append_line(
  209. 'raise ' + self.value.generate_python_use(code_generator))
  210. class CallInstruction(Instruction):
  211. """An instruction that performs a simple call."""
  212. def __init__(self, target, argument_list):
  213. Instruction.__init__(self)
  214. self.target = target
  215. self.argument_list = argument_list
  216. def simplify(self):
  217. """Applies basic simplification to this instruction and its children."""
  218. return CallInstruction(
  219. self.target.simplify(),
  220. [arg.simplify() for arg in self.argument_list])
  221. def generate_python_def(self, code_generator):
  222. """Generates Python code for this instruction."""
  223. if self.target.has_definition():
  224. self.target.generate_python_def(code_generator)
  225. for arg in self.argument_list:
  226. if arg.has_definition():
  227. arg.generate_python_def(code_generator)
  228. code_generator.append_line(
  229. '%s = %s(%s) ' % (
  230. code_generator.get_result_name(self),
  231. self.target.generate_python_use(code_generator),
  232. ', '.join([arg.generate_python_use(code_generator) for arg in self.argument_list])))
  233. class BinaryInstruction(Instruction):
  234. """An instruction that performs a binary operation."""
  235. def __init__(self, lhs, operator, rhs):
  236. Instruction.__init__(self)
  237. self.lhs = lhs
  238. self.operator = operator
  239. self.rhs = rhs
  240. def has_definition(self):
  241. """Tells if this instruction requires a definition."""
  242. return self.lhs.has_definition() or self.rhs.has_definition()
  243. def simplify(self):
  244. """Applies basic simplification to this instruction and its children."""
  245. simple_lhs, simple_rhs = self.lhs.simplify(), self.rhs.simplify()
  246. return BinaryInstruction(simple_lhs, self.operator, simple_rhs)
  247. def generate_python_use(self, code_generator):
  248. """Generates a Python expression that retrieves this instruction's
  249. result. The expression is returned as a string."""
  250. return '%s %s %s' % (
  251. self.lhs.generate_python_use(code_generator),
  252. self.operator,
  253. self.rhs.generate_python_use(code_generator))
  254. def generate_python_def(self, code_generator):
  255. """Generates a Python statement that executes this instruction.
  256. The statement is appended immediately to the code generator."""
  257. if self.lhs.has_definition():
  258. self.lhs.generate_python_def(code_generator)
  259. if self.rhs.has_definition():
  260. self.rhs.generate_python_def(code_generator)
  261. elif self.rhs.has_definition():
  262. self.rhs.generate_python_def(code_generator)
  263. else:
  264. code_generator.append_line('pass')
  265. class LoopInstruction(VoidInstruction):
  266. """Represents a loop-instruction, which loops until broken."""
  267. def __init__(self, body):
  268. VoidInstruction.__init__(self)
  269. self.body = body
  270. def simplify(self):
  271. """Applies basic simplification to this instruction and its children."""
  272. return LoopInstruction(self.body.simplify())
  273. def generate_python_def(self, code_generator):
  274. """Generates Python code for this instruction."""
  275. code_generator.append_line('while 1:')
  276. code_generator.increase_indentation()
  277. self.body.generate_python_def(code_generator)
  278. code_generator.decrease_indentation()
  279. class BreakInstruction(VoidInstruction):
  280. """Represents a break-instruction."""
  281. def generate_python_def(self, code_generator):
  282. """Generates Python code for this instruction."""
  283. code_generator.append_line('break')
  284. class ContinueInstruction(VoidInstruction):
  285. """Represents a continue-instruction."""
  286. def generate_python_def(self, code_generator):
  287. """Generates Python code for this instruction."""
  288. code_generator.append_line('continue')
  289. class CompoundInstruction(Instruction):
  290. """Represents an instruction that evaluates two other instructions
  291. in order, and returns the second instruction's result."""
  292. def __init__(self, first, second):
  293. Instruction.__init__(self)
  294. self.first = first
  295. self.second = second
  296. def has_result(self):
  297. """Tells if this instruction has a result."""
  298. return self.second.has_result()
  299. def simplify(self):
  300. """Applies basic simplification to this instruction and its children."""
  301. simple_fst, simple_snd = self.first.simplify(), self.second.simplify()
  302. if not simple_fst.has_definition():
  303. return simple_snd
  304. elif (not simple_snd.has_definition()) and (not simple_snd.has_result()):
  305. return simple_fst
  306. else:
  307. return CompoundInstruction(simple_fst, simple_snd)
  308. def generate_python_def(self, code_generator):
  309. """Generates Python code for this instruction."""
  310. self.first.generate_python_def(code_generator)
  311. code_generator.append_move_definition(self, self.second)
  312. class LiteralInstruction(Instruction):
  313. """Represents an integer, floating-point, string or Boolean literal."""
  314. def __init__(self, literal):
  315. Instruction.__init__(self)
  316. self.literal = literal
  317. def has_definition(self):
  318. """Tells if this instruction requires a definition."""
  319. return False
  320. def generate_python_use(self, code_generator):
  321. """Generates a Python expression that retrieves this instruction's
  322. result. The expression is returned as a string."""
  323. return repr(self.literal)
  324. class StateInstruction(Instruction):
  325. """An instruction that accesses the modelverse state."""
  326. def get_opcode(self):
  327. """Gets the opcode for this state instruction."""
  328. raise NotImplementedError()
  329. def get_arguments(self):
  330. """Gets this state instruction's argument list."""
  331. raise NotImplementedError()
  332. def generate_python_def(self, code_generator):
  333. """Generates a Python statement that executes this instruction.
  334. The statement is appended immediately to the code generator."""
  335. args = self.get_arguments()
  336. for arg_i in args:
  337. if arg_i.has_definition():
  338. arg_i.generate_python_def(code_generator)
  339. code_generator.append_state_definition(self, self.get_opcode(), args)
  340. class LocalInstruction(Instruction):
  341. """A base class for instructions that access local variables."""
  342. def __init__(self, name):
  343. Instruction.__init__(self)
  344. self.name = name
  345. def get_result_name_override(self):
  346. """Gets a value that overrides the code generator's result name for this
  347. instruction if it is not None."""
  348. return self.name
  349. def create_load(self):
  350. """Creates an instruction that loads the variable referenced by this instruction."""
  351. return LoadLocalInstruction(self.name)
  352. def create_store(self, value):
  353. """Creates an instruction that stores the given value in the variable referenced
  354. by this instruction."""
  355. return StoreLocalInstruction(self.name, value)
  356. def generate_python_use(self, code_generator):
  357. """Generates a Python expression that retrieves this instruction's
  358. result. The expression is returned as a string."""
  359. return self.name
  360. class StoreLocalInstruction(LocalInstruction):
  361. """An instruction that stores a value in a local variable."""
  362. def __init__(self, name, value):
  363. LocalInstruction.__init__(self, name)
  364. self.value = value
  365. def simplify(self):
  366. """Applies basic simplification to this instruction and its children."""
  367. return StoreLocalInstruction(self.name, self.value.simplify())
  368. def generate_python_def(self, code_generator):
  369. """Generates a Python statement that executes this instruction.
  370. The statement is appended immediately to the code generator."""
  371. code_generator.append_move_definition(self, self.value)
  372. class LoadLocalInstruction(LocalInstruction):
  373. """An instruction that loads a value from a local variable."""
  374. def has_definition(self):
  375. """Tells if this instruction requires a definition."""
  376. return False
  377. class DefineFunctionInstruction(LocalInstruction):
  378. """An instruction that defines a function."""
  379. def __init__(self, name, parameter_list, body):
  380. LocalInstruction.__init__(self, name)
  381. self.parameter_list = parameter_list
  382. self.body = body
  383. def generate_python_def(self, code_generator):
  384. """Generates a Python statement that executes this instruction.
  385. The statement is appended immediately to the code generator."""
  386. code_generator.append_line('def %s(%s):' % (self.name, ', '.join(self.parameter_list)))
  387. code_generator.increase_indentation()
  388. self.body.generate_python_def(code_generator)
  389. code_generator.decrease_indentation()
  390. class LocalExistsInstruction(LocalInstruction):
  391. """An instruction that checks if a local variable exists."""
  392. def has_definition(self):
  393. """Tells if this instruction requires a definition."""
  394. return False
  395. def generate_python_use(self, code_generator):
  396. """Generates a Python expression that retrieves this instruction's
  397. result. The expression is returned as a string."""
  398. return "'%s' in locals()" % self.name
  399. class LoadIndexInstruction(Instruction):
  400. """An instruction that produces a value by indexing a specified expression with
  401. a given key."""
  402. def __init__(self, indexed, key):
  403. Instruction.__init__(self)
  404. self.indexed = indexed
  405. self.key = key
  406. def has_definition(self):
  407. """Tells if this instruction requires a definition."""
  408. return False
  409. def generate_python_use(self, code_generator):
  410. """Generates a Python expression that retrieves this instruction's
  411. result. The expression is returned as a string."""
  412. if self.indexed.has_definition():
  413. self.indexed.generate_python_def(code_generator)
  414. if self.key.has_definition():
  415. self.key.generate_python_def(code_generator)
  416. return "%s[%s]" % (
  417. self.indexed.generate_python_use(code_generator),
  418. self.key.generate_python_use(code_generator))
  419. class NopInstruction(Instruction):
  420. """A nop instruction, which allows for the kernel's thread of execution to be interrupted."""
  421. def has_result(self):
  422. """Tells if this instruction computes a result."""
  423. return False
  424. def generate_python_def(self, code_generator):
  425. """Generates a Python statement that executes this instruction.
  426. The statement is appended immediately to the code generator."""
  427. code_generator.append_line('yield %s' % repr(NOP_LITERAL))
  428. class ReadValueInstruction(StateInstruction):
  429. """An instruction that reads a value from a node."""
  430. def __init__(self, node_id):
  431. StateInstruction.__init__(self)
  432. self.node_id = node_id
  433. def simplify(self):
  434. """Applies basic simplification to this instruction and its children."""
  435. return ReadValueInstruction(self.node_id.simplify())
  436. def get_opcode(self):
  437. """Gets the opcode for this state instruction."""
  438. return "RV"
  439. def get_arguments(self):
  440. """Gets this state instruction's argument list."""
  441. return [self.node_id]
  442. class ReadDictionaryValueInstruction(StateInstruction):
  443. """An instruction that reads a dictionary value."""
  444. def __init__(self, node_id, key):
  445. StateInstruction.__init__(self)
  446. self.node_id = node_id
  447. self.key = key
  448. def simplify(self):
  449. """Applies basic simplification to this instruction and its children."""
  450. return ReadDictionaryValueInstruction(
  451. self.node_id.simplify(),
  452. self.key.simplify())
  453. def get_opcode(self):
  454. """Gets the opcode for this state instruction."""
  455. return "RD"
  456. def get_arguments(self):
  457. """Gets this state instruction's argument list."""
  458. return [self.node_id, self.key]
  459. class ReadDictionaryEdgeInstruction(StateInstruction):
  460. """An instruction that reads a dictionary edge."""
  461. def __init__(self, node_id, key):
  462. StateInstruction.__init__(self)
  463. self.node_id = node_id
  464. self.key = key
  465. def simplify(self):
  466. """Applies basic simplification to this instruction and its children."""
  467. return ReadDictionaryEdgeInstruction(
  468. self.node_id.simplify(),
  469. self.key.simplify())
  470. def get_opcode(self):
  471. """Gets the opcode for this state instruction."""
  472. return "RDE"
  473. def get_arguments(self):
  474. """Gets this state instruction's argument list."""
  475. return [self.node_id, self.key]
  476. class CreateNodeInstruction(StateInstruction):
  477. """An instruction that creates an empty node."""
  478. def get_opcode(self):
  479. """Gets the opcode for this state instruction."""
  480. return "CN"
  481. def get_arguments(self):
  482. """Gets this state instruction's argument list."""
  483. return []
  484. class CreateDictionaryEdgeInstruction(StateInstruction):
  485. """An instruction that creates a dictionary edge."""
  486. def __init__(self, source_id, key, target_id):
  487. StateInstruction.__init__(self)
  488. self.source_id = source_id
  489. self.key = key
  490. self.target_id = target_id
  491. def simplify(self):
  492. """Applies basic simplification to this instruction and its children."""
  493. return CreateDictionaryEdgeInstruction(
  494. self.source_id.simplify(),
  495. self.key.simplify(),
  496. self.target_id.simplify())
  497. def get_opcode(self):
  498. """Gets the opcode for this state instruction."""
  499. return "CD"
  500. def get_arguments(self):
  501. """Gets this state instruction's argument list."""
  502. return [self.source_id, self.key, self.target_id]
  503. class DeleteEdgeInstruction(StateInstruction):
  504. """An instruction that deletes an edge."""
  505. def __init__(self, edge_id):
  506. StateInstruction.__init__(self)
  507. self.edge_id = edge_id
  508. def simplify(self):
  509. """Applies basic simplification to this instruction and its children."""
  510. return DeleteEdgeInstruction(self.edge_id.simplify())
  511. def has_result(self):
  512. """Tells if this instruction computes a result."""
  513. return False
  514. def get_opcode(self):
  515. """Gets the opcode for this state instruction."""
  516. return "DE"
  517. def get_arguments(self):
  518. """Gets this state instruction's argument list."""
  519. return [self.edge_id]
  520. def create_block(*statements):
  521. """Creates a block-statement from the given list of statements."""
  522. length = len(statements)
  523. if length == 0:
  524. return EmptyInstruction()
  525. elif length == 1:
  526. return statements[0]
  527. else:
  528. return CompoundInstruction(
  529. statements[0],
  530. create_block(*statements[1:]))
  531. if __name__ == "__main__":
  532. example_tree = SelectInstruction(
  533. LiteralInstruction(True),
  534. LoopInstruction(
  535. CompoundInstruction(
  536. BreakInstruction(),
  537. CompoundInstruction(
  538. EmptyInstruction(),
  539. ContinueInstruction()
  540. )
  541. )
  542. ),
  543. ReturnInstruction(
  544. EmptyInstruction()))
  545. print(example_tree.simplify())