tree_ir.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  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 generate_python_def(self, code_generator):
  41. """Generates a Python statement that executes this instruction.
  42. The statement is appended immediately to the code generator."""
  43. if self.has_definition():
  44. raise NotImplementedError()
  45. else:
  46. code_generator.append_line('pass')
  47. def generate_python_use(self, code_generator):
  48. """Generates a Python expression that retrieves this instruction's
  49. result. The expression is returned as a string."""
  50. if self.has_result():
  51. return code_generator.get_result_name(self)
  52. else:
  53. return 'None'
  54. def simplify(self):
  55. """Applies basic simplification to this instruction and its children."""
  56. return self
  57. def __str__(self):
  58. code_generator = PythonGenerator()
  59. self.generate_python_def(code_generator)
  60. return code_generator.code
  61. class PythonGenerator(object):
  62. """Generates Python code from instructions."""
  63. def __init__(self):
  64. self.code = ''
  65. self.indentation_string = ' ' * 4
  66. self.indentation = 0
  67. self.result_value_dict = {}
  68. def append(self, text):
  69. """Appends the given string to this code generator."""
  70. self.code += text
  71. def append_indentation(self):
  72. """Appends indentation to the code generator."""
  73. self.append(self.indentation_string * self.indentation)
  74. def append_line(self, line=None):
  75. """Appends the indentation string followed by the given string (if any)
  76. and a newline to the code generator."""
  77. self.append_indentation()
  78. if line is not None:
  79. self.append(line)
  80. self.append('\n')
  81. def increase_indentation(self):
  82. """Increases the code generator's indentation by one indent."""
  83. self.indentation += 1
  84. def decrease_indentation(self):
  85. """Decreases the code generator's indentation by one indent."""
  86. self.indentation -= 1
  87. def get_result_name(self, instruction):
  88. """Gets the name of the given instruction's result variable."""
  89. if instruction not in self.result_value_dict:
  90. self.result_value_dict[instruction] = \
  91. 'tmp' + str(len(self.result_value_dict))
  92. return self.result_value_dict[instruction]
  93. def append_definition(self, lhs, rhs):
  94. """Defines the first instruction's result variable as the second
  95. instruction's result."""
  96. self.append_line(
  97. self.get_result_name(lhs) + ' = ' + rhs.generate_python_use(self))
  98. def append_state_definition(self, lhs, opcode, args):
  99. """Appends a definition that queries the modelverse state."""
  100. self.append_line(
  101. "%s, = yield [('%s', [%s])]" % (
  102. self.get_result_name(lhs),
  103. opcode,
  104. ', '.join([arg_i.generate_python_use(self) for arg_i in args])))
  105. class VoidInstruction(Instruction):
  106. """A base class for instructions that do not return a value."""
  107. def has_result(self):
  108. """Tells if this instruction computes a result."""
  109. return False
  110. class EmptyInstruction(VoidInstruction):
  111. """Represents the empty instruction, which does nothing."""
  112. def has_definition(self):
  113. """Tells if this instruction requires a definition."""
  114. return False
  115. class SelectInstruction(Instruction):
  116. """Represents a select-instruction: an instruction that defines one of two
  117. child instructions, and sets its result to the defined child's result."""
  118. def __init__(self, condition, if_clause, else_clause):
  119. Instruction.__init__(self)
  120. self.condition = condition
  121. self.if_clause = if_clause
  122. self.else_clause = else_clause
  123. def has_result(self):
  124. """Tells if this instruction computes a result."""
  125. return self.if_clause.has_result() or self.else_clause.has_result()
  126. def simplify(self):
  127. """Applies basic simplification to this instruction and its children."""
  128. simple_cond = self.condition.simplify()
  129. simple_if = self.if_clause.simplify()
  130. simple_else = self.else_clause.simplify()
  131. if isinstance(simple_cond, LiteralInstruction):
  132. return simple_if if simple_cond.literal else simple_else
  133. else:
  134. return SelectInstruction(simple_cond, simple_if, simple_else)
  135. def generate_python_def(self, code_generator):
  136. """Generates Python code for this instruction."""
  137. if_has_result = self.has_result()
  138. if self.condition.has_definition():
  139. self.condition.generate_python_def(code_generator)
  140. code_generator.append_line(
  141. 'if ' + self.condition.generate_python_use(code_generator) + ':')
  142. code_generator.increase_indentation()
  143. if self.if_clause.has_definition() or (not if_has_result):
  144. self.if_clause.generate_python_def(code_generator)
  145. if if_has_result:
  146. code_generator.append_definition(self, self.if_clause)
  147. code_generator.decrease_indentation()
  148. else_has_def = self.else_clause.has_definition()
  149. if else_has_def or if_has_result:
  150. code_generator.append_line('else:')
  151. code_generator.increase_indentation()
  152. if else_has_def:
  153. self.else_clause.generate_python_def(code_generator)
  154. if if_has_result:
  155. code_generator.append_definition(self, self.else_clause)
  156. code_generator.decrease_indentation()
  157. class ReturnInstruction(VoidInstruction):
  158. """Represents a return-instruction."""
  159. def __init__(self, value):
  160. VoidInstruction.__init__(self)
  161. self.value = value
  162. def simplify(self):
  163. """Applies basic simplification to this instruction and its children."""
  164. return ReturnInstruction(self.value.simplify())
  165. def generate_python_def(self, code_generator):
  166. """Generates Python code for this instruction."""
  167. self.value.generate_python_def(code_generator)
  168. code_generator.append_line(
  169. 'raise PrimitiveFinished(' +
  170. self.value.generate_python_use(code_generator) +
  171. ')')
  172. class RaiseInstruction(VoidInstruction):
  173. """An instruction that raises an error."""
  174. def __init__(self, value):
  175. VoidInstruction.__init__(self)
  176. self.value = value
  177. def simplify(self):
  178. """Applies basic simplification to this instruction and its children."""
  179. return RaiseInstruction(self.value.simplify())
  180. def generate_python_def(self, code_generator):
  181. """Generates Python code for this instruction."""
  182. self.value.generate_python_def(code_generator)
  183. code_generator.append_line(
  184. 'raise ' + self.value.generate_python_use(code_generator))
  185. class CallInstruction(Instruction):
  186. """An instruction that performs a simple call."""
  187. def __init__(self, target, argument_list):
  188. Instruction.__init__(self)
  189. self.target = target
  190. self.argument_list = argument_list
  191. def simplify(self):
  192. """Applies basic simplification to this instruction and its children."""
  193. return CallInstruction(
  194. self.target.simplify(),
  195. [arg.simplify() for arg in self.argument_list])
  196. def generate_python_def(self, code_generator):
  197. """Generates Python code for this instruction."""
  198. if self.target.has_definition():
  199. self.target.generate_python_def(code_generator)
  200. for arg in self.argument_list:
  201. if arg.has_definition():
  202. arg.generate_python_def(code_generator)
  203. code_generator.append_line(
  204. '%s = %s(%s) ' % (
  205. code_generator.get_result_name(self),
  206. self.target.generate_python_use(code_generator),
  207. ', '.join([arg.generate_python_use(code_generator) for arg in self.argument_list])))
  208. class BinaryInstruction(Instruction):
  209. """An instruction that performs a binary operation."""
  210. def __init__(self, lhs, operator, rhs):
  211. Instruction.__init__(self)
  212. self.lhs = lhs
  213. self.operator = operator
  214. self.rhs = rhs
  215. def has_definition(self):
  216. """Tells if this instruction requires a definition."""
  217. return self.lhs.has_definition() or self.rhs.has_definition()
  218. def simplify(self):
  219. """Applies basic simplification to this instruction and its children."""
  220. simple_lhs, simple_rhs = self.lhs.simplify(), self.rhs.simplify()
  221. return BinaryInstruction(simple_lhs, self.operator, simple_rhs)
  222. def generate_python_use(self, code_generator):
  223. """Generates a Python expression that retrieves this instruction's
  224. result. The expression is returned as a string."""
  225. return '%s %s %s' % (
  226. self.lhs.generate_python_use(code_generator),
  227. self.operator,
  228. self.rhs.generate_python_use(code_generator))
  229. def generate_python_def(self, code_generator):
  230. """Generates a Python statement that executes this instruction.
  231. The statement is appended immediately to the code generator."""
  232. if self.lhs.has_definition():
  233. self.lhs.generate_python_def(code_generator)
  234. if self.rhs.has_definition():
  235. self.rhs.generate_python_def(code_generator)
  236. elif self.rhs.has_definition():
  237. self.rhs.generate_python_def(code_generator)
  238. else:
  239. code_generator.append_line('pass')
  240. class LoopInstruction(VoidInstruction):
  241. """Represents a loop-instruction, which loops until broken."""
  242. def __init__(self, body):
  243. VoidInstruction.__init__(self)
  244. self.body = body
  245. def simplify(self):
  246. """Applies basic simplification to this instruction and its children."""
  247. return LoopInstruction(self.body.simplify())
  248. def generate_python_def(self, code_generator):
  249. """Generates Python code for this instruction."""
  250. code_generator.append_line('while True:')
  251. code_generator.increase_indentation()
  252. self.body.generate_python_def(code_generator)
  253. code_generator.decrease_indentation()
  254. class BreakInstruction(VoidInstruction):
  255. """Represents a break-instruction."""
  256. def generate_python_def(self, code_generator):
  257. """Generates Python code for this instruction."""
  258. code_generator.append_line('break')
  259. class ContinueInstruction(VoidInstruction):
  260. """Represents a continue-instruction."""
  261. def generate_python_def(self, code_generator):
  262. """Generates Python code for this instruction."""
  263. code_generator.append_line('continue')
  264. class CompoundInstruction(Instruction):
  265. """Represents an instruction that evaluates two other instructions
  266. in order, and returns the second instruction's result."""
  267. def __init__(self, first, second):
  268. Instruction.__init__(self)
  269. self.first = first
  270. self.second = second
  271. def has_result(self):
  272. """Tells if this instruction has a result."""
  273. return self.second.has_result()
  274. def simplify(self):
  275. """Applies basic simplification to this instruction and its children."""
  276. simple_fst, simple_snd = self.first.simplify(), self.second.simplify()
  277. if not simple_fst.has_definition():
  278. return simple_snd
  279. elif (not simple_snd.has_definition()) and (not simple_snd.has_result()):
  280. return simple_fst
  281. else:
  282. return CompoundInstruction(simple_fst, simple_snd)
  283. def generate_python_def(self, code_generator):
  284. """Generates Python code for this instruction."""
  285. self.first.generate_python_def(code_generator)
  286. self.second.generate_python_def(code_generator)
  287. if self.has_result():
  288. code_generator.append_definition(self, self.second)
  289. class LiteralInstruction(Instruction):
  290. """Represents an integer, floating-point, string or Boolean literal."""
  291. def __init__(self, literal):
  292. Instruction.__init__(self)
  293. self.literal = literal
  294. def has_definition(self):
  295. """Tells if this instruction requires a definition."""
  296. return False
  297. def generate_python_use(self, code_generator):
  298. """Generates a Python expression that retrieves this instruction's
  299. result. The expression is returned as a string."""
  300. return repr(self.literal)
  301. class StateInstruction(Instruction):
  302. """An instruction that accesses the modelverse state."""
  303. def get_opcode(self):
  304. """Gets the opcode for this state instruction."""
  305. raise NotImplementedError()
  306. def get_arguments(self):
  307. """Gets this state instruction's argument list."""
  308. raise NotImplementedError()
  309. def generate_python_def(self, code_generator):
  310. """Generates a Python statement that executes this instruction.
  311. The statement is appended immediately to the code generator."""
  312. args = self.get_arguments()
  313. for arg_i in args:
  314. if arg_i.has_definition():
  315. arg_i.generate_python_def(code_generator)
  316. code_generator.append_state_definition(self, self.get_opcode(), args)
  317. class LocalInstruction(Instruction):
  318. """A base class for instructions that access local variables."""
  319. def __init__(self, name):
  320. Instruction.__init__(self)
  321. self.name = name
  322. def create_load(self):
  323. """Creates an instruction that loads the variable referenced by this instruction."""
  324. return LoadLocalInstruction(self.name)
  325. def create_store(self, value):
  326. """Creates an instruction that stores the given value in the variable referenced
  327. by this instruction."""
  328. return StoreLocalInstruction(self.name, value)
  329. def generate_python_use(self, code_generator):
  330. """Generates a Python expression that retrieves this instruction's
  331. result. The expression is returned as a string."""
  332. return self.name
  333. class StoreLocalInstruction(LocalInstruction):
  334. """An instruction that stores a value in a local variable."""
  335. def __init__(self, name, value):
  336. LocalInstruction.__init__(self, name)
  337. self.value = value
  338. def simplify(self):
  339. """Applies basic simplification to this instruction and its children."""
  340. return StoreLocalInstruction(self.name, self.value.simplify())
  341. def generate_python_def(self, code_generator):
  342. """Generates a Python statement that executes this instruction.
  343. The statement is appended immediately to the code generator."""
  344. if self.value.has_definition():
  345. self.value.generate_python_def(code_generator)
  346. code_generator.append_line(
  347. '%s = %s' % (self.name, self.value.generate_python_use(code_generator)))
  348. class LoadLocalInstruction(LocalInstruction):
  349. """An instruction that loads a value from a local variable."""
  350. def has_definition(self):
  351. """Tells if this instruction requires a definition."""
  352. return False
  353. class DefineFunctionInstruction(LocalInstruction):
  354. """An instruction that defines a function."""
  355. def __init__(self, name, parameter_list, body):
  356. LocalInstruction.__init__(self, name)
  357. self.parameter_list = parameter_list
  358. self.body = body
  359. def generate_python_def(self, code_generator):
  360. """Generates a Python statement that executes this instruction.
  361. The statement is appended immediately to the code generator."""
  362. code_generator.append_line('def %s(%s):' % (self.name, ', '.join(self.parameter_list)))
  363. code_generator.increase_indentation()
  364. self.body.generate_python_def(code_generator)
  365. code_generator.decrease_indentation()
  366. class LocalExistsInstruction(LocalInstruction):
  367. """An instruction that checks if a local variable exists."""
  368. def has_definition(self):
  369. """Tells if this instruction requires a definition."""
  370. return False
  371. def generate_python_use(self, code_generator):
  372. """Generates a Python expression that retrieves this instruction's
  373. result. The expression is returned as a string."""
  374. return "'%s' in locals()" % self.name
  375. class LoadIndexInstruction(Instruction):
  376. """An instruction that produces a value by indexing a specified expression with
  377. a given key."""
  378. def __init__(self, indexed, key):
  379. Instruction.__init__(self)
  380. self.indexed = indexed
  381. self.key = key
  382. def has_definition(self):
  383. """Tells if this instruction requires a definition."""
  384. return False
  385. def generate_python_use(self, code_generator):
  386. """Generates a Python expression that retrieves this instruction's
  387. result. The expression is returned as a string."""
  388. if self.indexed.has_definition():
  389. self.indexed.generate_python_def(code_generator)
  390. if self.key.has_definition():
  391. self.key.generate_python_def(code_generator)
  392. return "%s[%s]" % (
  393. self.indexed.generate_python_use(code_generator),
  394. self.key.generate_python_use(code_generator))
  395. class NopInstruction(Instruction):
  396. """A nop instruction, which allows for the kernel's thread of execution to be interrupted."""
  397. def has_result(self):
  398. """Tells if this instruction computes a result."""
  399. return False
  400. def generate_python_def(self, code_generator):
  401. """Generates a Python statement that executes this instruction.
  402. The statement is appended immediately to the code generator."""
  403. code_generator.append_line('yield %s' % repr(NOP_LITERAL))
  404. class ReadValueInstruction(StateInstruction):
  405. """An instruction that reads a value from a node."""
  406. def __init__(self, node_id):
  407. StateInstruction.__init__(self)
  408. self.node_id = node_id
  409. def simplify(self):
  410. """Applies basic simplification to this instruction and its children."""
  411. return ReadValueInstruction(self.node_id.simplify())
  412. def get_opcode(self):
  413. """Gets the opcode for this state instruction."""
  414. return "RV"
  415. def get_arguments(self):
  416. """Gets this state instruction's argument list."""
  417. return [self.node_id]
  418. class ReadDictionaryValueInstruction(StateInstruction):
  419. """An instruction that reads a dictionary value."""
  420. def __init__(self, node_id, key):
  421. StateInstruction.__init__(self)
  422. self.node_id = node_id
  423. self.key = key
  424. def simplify(self):
  425. """Applies basic simplification to this instruction and its children."""
  426. return ReadDictionaryValueInstruction(
  427. self.node_id.simplify(),
  428. self.key.simplify())
  429. def get_opcode(self):
  430. """Gets the opcode for this state instruction."""
  431. return "RD"
  432. def get_arguments(self):
  433. """Gets this state instruction's argument list."""
  434. return [self.node_id, self.key]
  435. class ReadDictionaryEdgeInstruction(StateInstruction):
  436. """An instruction that reads a dictionary edge."""
  437. def __init__(self, node_id, key):
  438. StateInstruction.__init__(self)
  439. self.node_id = node_id
  440. self.key = key
  441. def simplify(self):
  442. """Applies basic simplification to this instruction and its children."""
  443. return ReadDictionaryEdgeInstruction(
  444. self.node_id.simplify(),
  445. self.key.simplify())
  446. def get_opcode(self):
  447. """Gets the opcode for this state instruction."""
  448. return "RDE"
  449. def get_arguments(self):
  450. """Gets this state instruction's argument list."""
  451. return [self.node_id, self.key]
  452. class CreateNodeInstruction(StateInstruction):
  453. """An instruction that creates an empty node."""
  454. def get_opcode(self):
  455. """Gets the opcode for this state instruction."""
  456. return "CN"
  457. def get_arguments(self):
  458. """Gets this state instruction's argument list."""
  459. return []
  460. class CreateDictionaryEdgeInstruction(StateInstruction):
  461. """An instruction that creates a dictionary edge."""
  462. def __init__(self, source_id, key, target_id):
  463. StateInstruction.__init__(self)
  464. self.source_id = source_id
  465. self.key = key
  466. self.target_id = target_id
  467. def simplify(self):
  468. """Applies basic simplification to this instruction and its children."""
  469. return CreateDictionaryEdgeInstruction(
  470. self.source_id.simplify(),
  471. self.key.simplify(),
  472. self.target_id.simplify())
  473. def get_opcode(self):
  474. """Gets the opcode for this state instruction."""
  475. return "CD"
  476. def get_arguments(self):
  477. """Gets this state instruction's argument list."""
  478. return [self.source_id, self.key, self.target_id]
  479. class DeleteEdgeInstruction(StateInstruction):
  480. """An instruction that deletes an edge."""
  481. def __init__(self, edge_id):
  482. StateInstruction.__init__(self)
  483. self.edge_id = edge_id
  484. def simplify(self):
  485. """Applies basic simplification to this instruction and its children."""
  486. return DeleteEdgeInstruction(self.edge_id.simplify())
  487. def has_result(self):
  488. """Tells if this instruction computes a result."""
  489. return False
  490. def get_opcode(self):
  491. """Gets the opcode for this state instruction."""
  492. return "DE"
  493. def get_arguments(self):
  494. """Gets this state instruction's argument list."""
  495. return [self.edge_id]
  496. def create_block(*statements):
  497. """Creates a block-statement from the given list of statements."""
  498. length = len(statements)
  499. if length == 0:
  500. return EmptyInstruction()
  501. elif length == 1:
  502. return statements[0]
  503. else:
  504. return CompoundInstruction(
  505. create_block(*statements[:-1]),
  506. statements[-1])
  507. if __name__ == "__main__":
  508. example_tree = SelectInstruction(
  509. LiteralInstruction(True),
  510. LoopInstruction(
  511. CompoundInstruction(
  512. BreakInstruction(),
  513. CompoundInstruction(
  514. EmptyInstruction(),
  515. ContinueInstruction()
  516. )
  517. )
  518. ),
  519. ReturnInstruction(
  520. EmptyInstruction()))
  521. print(example_tree.simplify())