|
@@ -28,6 +28,8 @@
|
|
|
# Let's just agree to disagree on map vs list comprehensions, pylint.
|
|
|
# pylint: disable=I0011,W0141
|
|
|
|
|
|
+import modelverse_jit.source_map as source_map
|
|
|
+
|
|
|
NOP_LITERAL = None
|
|
|
"""A literal that results in a nop during which execution may be interrupted
|
|
|
when yielded."""
|
|
@@ -108,6 +110,10 @@ class Instruction(object):
|
|
|
instruction if it is not None."""
|
|
|
return None
|
|
|
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return True
|
|
|
+
|
|
|
def generate_python_def(self, code_generator):
|
|
|
"""Generates a Python statement that executes this instruction.
|
|
|
The statement is appended immediately to the code generator."""
|
|
@@ -150,7 +156,6 @@ class Instruction(object):
|
|
|
|
|
|
class PythonGenerator(object):
|
|
|
"""Generates Python code from instructions."""
|
|
|
-
|
|
|
def __init__(self, combine_state_definitions=True):
|
|
|
self.code = []
|
|
|
self.state_definitions = []
|
|
@@ -159,11 +164,14 @@ class PythonGenerator(object):
|
|
|
self.indentation = 0
|
|
|
self.result_name_dict = {}
|
|
|
self.combine_state_definitions = combine_state_definitions
|
|
|
+ self.source_map_builder = source_map.SourceMapBuilder()
|
|
|
|
|
|
def append(self, text):
|
|
|
"""Appends the given string to this code generator."""
|
|
|
self.flush_state_definitions()
|
|
|
self.code.append(text)
|
|
|
+ for _ in xrange(text.count('\n')):
|
|
|
+ self.source_map_builder.append_line()
|
|
|
|
|
|
def append_indentation(self):
|
|
|
"""Appends indentation to the code generator."""
|
|
@@ -225,7 +233,7 @@ class PythonGenerator(object):
|
|
|
# Generate the rhs' definition.
|
|
|
rhs.generate_python_def(self)
|
|
|
# Only perform an assignment if it's truly necessary.
|
|
|
- if lhs_result_name != rhs_result_name:
|
|
|
+ if lhs_result_name != rhs_result_name or not rhs.has_result_temporary():
|
|
|
self.append_definition(lhs, rhs)
|
|
|
else:
|
|
|
self.append_definition(lhs, rhs)
|
|
@@ -284,6 +292,9 @@ class EmptyInstruction(VoidInstruction):
|
|
|
"""Tells if this instruction requires a definition."""
|
|
|
return False
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "EmptyInstruction()"
|
|
|
+
|
|
|
class SelectInstruction(Instruction):
|
|
|
"""Represents a select-instruction: an instruction that defines one of two
|
|
|
child instructions, and sets its result to the defined child's result."""
|
|
@@ -315,14 +326,15 @@ class SelectInstruction(Instruction):
|
|
|
condition, if_clause, else_clause = new_children
|
|
|
return SelectInstruction(condition, if_clause, else_clause)
|
|
|
|
|
|
- def generate_python_def(self, code_generator):
|
|
|
+ def generate_python_if(self, code_generator, is_elif=False):
|
|
|
"""Generates Python code for this instruction."""
|
|
|
if_has_result = self.has_result()
|
|
|
if self.condition.has_definition():
|
|
|
self.condition.generate_python_def(code_generator)
|
|
|
|
|
|
code_generator.append_line(
|
|
|
- 'if ' + self.condition.generate_python_use(code_generator) + ':')
|
|
|
+ ('elif ' if is_elif else 'if ') +
|
|
|
+ self.condition.generate_python_use(code_generator) + ':')
|
|
|
code_generator.increase_indentation()
|
|
|
if if_has_result:
|
|
|
code_generator.append_move_definition(self, self.if_clause)
|
|
@@ -331,13 +343,28 @@ class SelectInstruction(Instruction):
|
|
|
code_generator.decrease_indentation()
|
|
|
else_has_def = self.else_clause.has_definition()
|
|
|
if else_has_def or if_has_result:
|
|
|
- code_generator.append_line('else:')
|
|
|
- code_generator.increase_indentation()
|
|
|
- if if_has_result:
|
|
|
- code_generator.append_move_definition(self, self.else_clause)
|
|
|
+ if (isinstance(self.else_clause, SelectInstruction) and
|
|
|
+ not self.else_clause.condition.has_definition()):
|
|
|
+ self.else_clause.generate_python_if(code_generator, True)
|
|
|
+ if if_has_result:
|
|
|
+ code_generator.increase_indentation()
|
|
|
+ code_generator.append_definition(self, self.else_clause)
|
|
|
+ code_generator.decrease_indentation()
|
|
|
else:
|
|
|
- self.else_clause.generate_python_def(code_generator)
|
|
|
- code_generator.decrease_indentation()
|
|
|
+ code_generator.append_line('else:')
|
|
|
+ code_generator.increase_indentation()
|
|
|
+ if if_has_result:
|
|
|
+ code_generator.append_move_definition(self, self.else_clause)
|
|
|
+ else:
|
|
|
+ self.else_clause.generate_python_def(code_generator)
|
|
|
+ code_generator.decrease_indentation()
|
|
|
+
|
|
|
+ def generate_python_def(self, code_generator):
|
|
|
+ """Generates Python code for this instruction."""
|
|
|
+ return self.generate_python_if(code_generator)
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return "SelectInstruction(%r, %r, %r)" % (self.condition, self.if_clause, self.else_clause)
|
|
|
|
|
|
class ReturnInstruction(VoidInstruction):
|
|
|
"""Represents a return-instruction."""
|
|
@@ -387,6 +414,9 @@ class ReturnInstruction(VoidInstruction):
|
|
|
self.value.generate_python_use(code_generator) +
|
|
|
')')
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "ReturnInstruction(%r)" % self.value
|
|
|
+
|
|
|
class RaiseInstruction(VoidInstruction):
|
|
|
"""An instruction that raises an error."""
|
|
|
def __init__(self, value):
|
|
@@ -408,6 +438,9 @@ class RaiseInstruction(VoidInstruction):
|
|
|
code_generator.append_line(
|
|
|
'raise ' + self.value.generate_python_use(code_generator))
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "RaiseInstruction(%r)" % self.value
|
|
|
+
|
|
|
class CallInstruction(Instruction):
|
|
|
"""An instruction that performs a simple call."""
|
|
|
def __init__(self, target, argument_list):
|
|
@@ -438,11 +471,15 @@ class CallInstruction(Instruction):
|
|
|
self.target.generate_python_use(code_generator),
|
|
|
', '.join([arg.generate_python_use(code_generator) for arg in self.argument_list])))
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "CallInstruction(%r, %r)" % (self.target, self.argument_list)
|
|
|
+
|
|
|
class PrintInstruction(VoidInstruction):
|
|
|
"""An instruction that prints a value."""
|
|
|
def __init__(self, argument):
|
|
|
VoidInstruction.__init__(self)
|
|
|
self.argument = argument
|
|
|
+ assert isinstance(argument, Instruction)
|
|
|
|
|
|
def get_children(self):
|
|
|
"""Gets this instruction's sequence of child instructions."""
|
|
@@ -462,6 +499,57 @@ class PrintInstruction(VoidInstruction):
|
|
|
'print(%s)' % (
|
|
|
self.argument.generate_python_use(code_generator)))
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "PrintInstruction(%r)" % self.argument
|
|
|
+
|
|
|
+class DebugInfoInstruction(Instruction):
|
|
|
+ """An instruction that defines debug information for its child instruction."""
|
|
|
+ def __init__(self, value, debug_info):
|
|
|
+ Instruction.__init__(self)
|
|
|
+ self.value = value
|
|
|
+ self.debug_info = debug_info
|
|
|
+
|
|
|
+ def has_definition_impl(self):
|
|
|
+ """Tells if this instruction requires a definition."""
|
|
|
+ return self.value.has_definition()
|
|
|
+
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return self.value.has_result_temporary()
|
|
|
+
|
|
|
+ def get_result_type_impl(self):
|
|
|
+ """Gets this instruction's result type."""
|
|
|
+ return self.value.get_result_type()
|
|
|
+
|
|
|
+ def get_result_name_override(self, code_generator):
|
|
|
+ """Gets a value that overrides the code generator's result name for this
|
|
|
+ instruction if it is not None."""
|
|
|
+ return self.value.get_result_name_override(code_generator)
|
|
|
+
|
|
|
+ def get_children(self):
|
|
|
+ """Gets this instruction's sequence of child instructions."""
|
|
|
+ return [self.value]
|
|
|
+
|
|
|
+ def create(self, new_children):
|
|
|
+ """Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
+ arg, = new_children
|
|
|
+ return DebugInfoInstruction(arg, self.debug_info)
|
|
|
+
|
|
|
+ def generate_python_def(self, code_generator):
|
|
|
+ """Generates Python code for this instruction."""
|
|
|
+ if self.has_definition():
|
|
|
+ code_generator.source_map_builder.push_debug_info(self.debug_info)
|
|
|
+ code_generator.append_move_definition(self, self.value)
|
|
|
+ code_generator.source_map_builder.pop_debug_info()
|
|
|
+
|
|
|
+ def generate_python_use(self, code_generator):
|
|
|
+ """Generates a Python expression that retrieves this instruction's
|
|
|
+ result. The expression is returned as a string."""
|
|
|
+ return self.value.generate_python_use(code_generator)
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return "DebugInfoInstruction(%r, %r)" % (self.value, self.debug_info)
|
|
|
+
|
|
|
class BinaryInstruction(Instruction):
|
|
|
"""An instruction that performs a binary operation."""
|
|
|
def __init__(self, lhs, operator, rhs):
|
|
@@ -492,6 +580,10 @@ class BinaryInstruction(Instruction):
|
|
|
else:
|
|
|
return self
|
|
|
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return False
|
|
|
+
|
|
|
def generate_python_use(self, code_generator):
|
|
|
"""Generates a Python expression that retrieves this instruction's
|
|
|
result. The expression is returned as a string."""
|
|
@@ -503,15 +595,19 @@ class BinaryInstruction(Instruction):
|
|
|
def generate_python_def(self, code_generator):
|
|
|
"""Generates a Python statement that executes this instruction.
|
|
|
The statement is appended immediately to the code generator."""
|
|
|
- if self.lhs.has_definition():
|
|
|
+ lhs_has_def, rhs_has_def = self.lhs.has_definition(), self.rhs.has_definition()
|
|
|
+ if lhs_has_def:
|
|
|
self.lhs.generate_python_def(code_generator)
|
|
|
- if self.rhs.has_definition():
|
|
|
+ if rhs_has_def:
|
|
|
self.rhs.generate_python_def(code_generator)
|
|
|
- elif self.rhs.has_definition():
|
|
|
+ elif rhs_has_def:
|
|
|
self.rhs.generate_python_def(code_generator)
|
|
|
else:
|
|
|
code_generator.append_line('pass')
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "BinaryInstruction(%r, %r, %r)" % (self.lhs, self.operator, self.rhs)
|
|
|
+
|
|
|
class UnaryInstruction(Instruction):
|
|
|
"""An instruction that performs a unary operation."""
|
|
|
def __init__(self, operator, operand):
|
|
@@ -527,6 +623,10 @@ class UnaryInstruction(Instruction):
|
|
|
"""Gets this instruction's sequence of child instructions."""
|
|
|
return [self.operand]
|
|
|
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return False
|
|
|
+
|
|
|
def create(self, new_children):
|
|
|
"""Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
operand, = new_children
|
|
@@ -556,9 +656,11 @@ class UnaryInstruction(Instruction):
|
|
|
else:
|
|
|
code_generator.append_line('pass')
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "UnaryInstruction(%r, %r)" % (self.operator, self.operand)
|
|
|
+
|
|
|
class LoopInstruction(VoidInstruction):
|
|
|
"""Represents a loop-instruction, which loops until broken."""
|
|
|
-
|
|
|
def __init__(self, body):
|
|
|
VoidInstruction.__init__(self)
|
|
|
self.body = body
|
|
@@ -579,18 +681,61 @@ class LoopInstruction(VoidInstruction):
|
|
|
self.body.generate_python_def(code_generator)
|
|
|
code_generator.decrease_indentation()
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "LoopInstruction(%r)" % self.body
|
|
|
+
|
|
|
class BreakInstruction(VoidInstruction):
|
|
|
"""Represents a break-instruction."""
|
|
|
def generate_python_def(self, code_generator):
|
|
|
"""Generates Python code for this instruction."""
|
|
|
code_generator.append_line('break')
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "BreakInstruction()"
|
|
|
+
|
|
|
class ContinueInstruction(VoidInstruction):
|
|
|
"""Represents a continue-instruction."""
|
|
|
def generate_python_def(self, code_generator):
|
|
|
"""Generates Python code for this instruction."""
|
|
|
code_generator.append_line('continue')
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "ContinueInstruction()"
|
|
|
+
|
|
|
+class IgnoreInstruction(VoidInstruction):
|
|
|
+ """Represents an instruction that evaluates its operand, and then discards
|
|
|
+ the result."""
|
|
|
+ def __init__(self, value):
|
|
|
+ VoidInstruction.__init__(self)
|
|
|
+ self.value = value
|
|
|
+
|
|
|
+ def has_definition_impl(self):
|
|
|
+ """Tells if this instruction requires a definition."""
|
|
|
+ return self.value.has_definition()
|
|
|
+
|
|
|
+ def get_children(self):
|
|
|
+ """Gets this instruction's sequence of child instructions."""
|
|
|
+ return [self.value]
|
|
|
+
|
|
|
+ def create(self, new_children):
|
|
|
+ """Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
+ value, = new_children
|
|
|
+ return IgnoreInstruction(value)
|
|
|
+
|
|
|
+ def simplify_node(self):
|
|
|
+ """Applies basic simplification to this instruction and its children."""
|
|
|
+ if not self.value.has_result():
|
|
|
+ return self.value
|
|
|
+ else:
|
|
|
+ return self
|
|
|
+
|
|
|
+ def generate_python_def(self, code_generator):
|
|
|
+ """Generates Python code for this instruction."""
|
|
|
+ self.value.generate_python_def(code_generator)
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return "IgnoreInstruction(%r)" % self.value
|
|
|
+
|
|
|
class CompoundInstruction(Instruction):
|
|
|
"""Represents an instruction that evaluates two other instructions
|
|
|
in order, and returns the second instruction's result."""
|
|
@@ -637,6 +782,9 @@ class CompoundInstruction(Instruction):
|
|
|
self.first.generate_python_def(code_generator)
|
|
|
self.second.generate_python_def(code_generator)
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "CompoundInstruction(%r, %r)" % (self.first, self.second)
|
|
|
+
|
|
|
class LiteralInstruction(Instruction):
|
|
|
"""Represents an integer, floating-point, string or Boolean literal."""
|
|
|
def __init__(self, literal):
|
|
@@ -647,6 +795,10 @@ class LiteralInstruction(Instruction):
|
|
|
"""Tells if this instruction requires a definition."""
|
|
|
return False
|
|
|
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return False
|
|
|
+
|
|
|
def get_children(self):
|
|
|
"""Gets this instruction's sequence of child instructions."""
|
|
|
return []
|
|
@@ -660,6 +812,9 @@ class LiteralInstruction(Instruction):
|
|
|
result. The expression is returned as a string."""
|
|
|
return repr(self.literal)
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "LiteralInstruction(%r)" % self.literal
|
|
|
+
|
|
|
class DictionaryLiteralInstruction(Instruction):
|
|
|
"""Constructs a dictionary literal."""
|
|
|
def __init__(self, key_value_pairs):
|
|
@@ -668,12 +823,18 @@ class DictionaryLiteralInstruction(Instruction):
|
|
|
|
|
|
def get_children(self):
|
|
|
"""Gets this instruction's sequence of child instructions."""
|
|
|
- return [val for _, val in self.key_value_pairs]
|
|
|
+ results = []
|
|
|
+ for key, val in self.key_value_pairs:
|
|
|
+ results.append(key)
|
|
|
+ results.append(val)
|
|
|
+ return results
|
|
|
|
|
|
def create(self, new_children):
|
|
|
"""Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
- keys = [k for k, _ in self.key_value_pairs]
|
|
|
- return DictionaryLiteralInstruction(zip(keys, new_children))
|
|
|
+ new_kv_pairs = []
|
|
|
+ for i in xrange(len(self.key_value_pairs)):
|
|
|
+ new_kv_pairs.append((new_children[2 * i], new_children[2 * i + 1]))
|
|
|
+ return DictionaryLiteralInstruction(new_kv_pairs)
|
|
|
|
|
|
def has_definition_impl(self):
|
|
|
"""Tells if this instruction requires a definition."""
|
|
@@ -681,42 +842,92 @@ class DictionaryLiteralInstruction(Instruction):
|
|
|
[key.has_definition() or val.has_definition()
|
|
|
for key, val in self.key_value_pairs])
|
|
|
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return False
|
|
|
+
|
|
|
def simplify(self):
|
|
|
"""Applies basic simplification to this instruction and its children."""
|
|
|
return DictionaryLiteralInstruction(
|
|
|
[(key.simplify(), val.simplify()) for key, val in self.key_value_pairs])
|
|
|
|
|
|
- def generate_dictionary_expr(self, code_generator):
|
|
|
- """Generates an expression that creates this dictionary."""
|
|
|
+ def generate_python_def(self, code_generator):
|
|
|
+ """Generates a Python statement that executes this instruction.
|
|
|
+ The statement is appended immediately to the code generator."""
|
|
|
+ for key, val in self.key_value_pairs:
|
|
|
+ if key.has_definition():
|
|
|
+ key.generate_python_def(code_generator)
|
|
|
+ if val.has_definition():
|
|
|
+ val.generate_python_def(code_generator)
|
|
|
+
|
|
|
+ def generate_python_use(self, code_generator):
|
|
|
+ """Generates a Python expression that retrieves this instruction's
|
|
|
+ result. The expression is returned as a string."""
|
|
|
return '{%s}' % ', '.join(
|
|
|
['%s : %s' % (
|
|
|
key.generate_python_use(code_generator),
|
|
|
val.generate_python_use(code_generator))
|
|
|
for key, val in self.key_value_pairs])
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "DictionaryLiteralInstruction(%r)" % self.key_value_pairs
|
|
|
+
|
|
|
+class ListSliceInstruction(Instruction):
|
|
|
+ """Slices a list."""
|
|
|
+ def __init__(self, seq, start, end, step):
|
|
|
+ Instruction.__init__(self)
|
|
|
+ self.seq = seq
|
|
|
+ self.start = start
|
|
|
+ self.end = end
|
|
|
+ self.step = step
|
|
|
+
|
|
|
+ def get_children(self):
|
|
|
+ """Gets this instruction's sequence of child instructions."""
|
|
|
+ all_items = (self.seq, self.start, self.end, self.step)
|
|
|
+ return [item for item in all_items if item is not None]
|
|
|
+
|
|
|
+ def create(self, new_children):
|
|
|
+ """Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
+ # pylint: disable=I0011,E1120
|
|
|
+ args = []
|
|
|
+ i = 0
|
|
|
+ for old_item in (self.seq, self.start, self.end, self.step):
|
|
|
+ if old_item is None:
|
|
|
+ args.append(None)
|
|
|
+ else:
|
|
|
+ args.append(new_children[i])
|
|
|
+ i += 1
|
|
|
+
|
|
|
+ assert len(new_children) == i
|
|
|
+ assert len(args) == 4
|
|
|
+ return ListSliceInstruction(*args)
|
|
|
+
|
|
|
+ def has_definition_impl(self):
|
|
|
+ """Tells if this instruction requires a definition."""
|
|
|
+ return any([item.has_definition() for item in self.get_children()])
|
|
|
+
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return False
|
|
|
+
|
|
|
def generate_python_def(self, code_generator):
|
|
|
"""Generates a Python statement that executes this instruction.
|
|
|
The statement is appended immediately to the code generator."""
|
|
|
- if self.has_definition():
|
|
|
- for key, val in self.key_value_pairs:
|
|
|
- if key.has_definition():
|
|
|
- key.generate_python_def(code_generator)
|
|
|
- if val.has_definition():
|
|
|
- val.generate_python_def(code_generator)
|
|
|
-
|
|
|
- code_generator.append_line('%s = %s' % (
|
|
|
- code_generator.get_result_name(self),
|
|
|
- self.generate_dictionary_expr(code_generator)))
|
|
|
- else:
|
|
|
- code_generator.append_line('pass')
|
|
|
+ for item in self.get_children():
|
|
|
+ if item.has_definition():
|
|
|
+ item.generate_python_def(code_generator)
|
|
|
|
|
|
def generate_python_use(self, code_generator):
|
|
|
"""Generates a Python expression that retrieves this instruction's
|
|
|
result. The expression is returned as a string."""
|
|
|
- if self.has_definition():
|
|
|
- return code_generator.get_result_name(self)
|
|
|
- else:
|
|
|
- return self.generate_dictionary_expr(code_generator)
|
|
|
+ return '%s[%s:%s:%s]' % (
|
|
|
+ self.seq.generate_python_use(code_generator),
|
|
|
+ '' if self.start is None else self.start.generate_python_use(code_generator),
|
|
|
+ '' if self.end is None else self.end.generate_python_use(code_generator),
|
|
|
+ '' if self.step is None else self.step.generate_python_use(code_generator))
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return "ListSliceInstruction(%r, %r, %r, %r)" % (self.seq, self.start, self.end, self.step)
|
|
|
|
|
|
class StateInstruction(Instruction):
|
|
|
"""An instruction that accesses the modelverse state."""
|
|
@@ -743,7 +954,6 @@ class StateInstruction(Instruction):
|
|
|
def generate_python_def(self, code_generator):
|
|
|
"""Generates a Python statement that executes this instruction.
|
|
|
The statement is appended immediately to the code generator."""
|
|
|
-
|
|
|
args = self.get_arguments()
|
|
|
for arg_i in args:
|
|
|
if arg_i.has_definition():
|
|
@@ -751,6 +961,9 @@ class StateInstruction(Instruction):
|
|
|
|
|
|
code_generator.append_state_definition(self, self.get_opcode(), args)
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return "StateInstruction(%s)" % ', '.join(map(repr, self.get_arguments()))
|
|
|
+
|
|
|
class RunGeneratorFunctionInstruction(StateInstruction):
|
|
|
"""An instruction that runs a generator function."""
|
|
|
def __init__(self, function, argument_dict, result_type=PRIMITIVE_RESULT_TYPE):
|
|
@@ -774,10 +987,27 @@ class RunGeneratorFunctionInstruction(StateInstruction):
|
|
|
|
|
|
class RunTailGeneratorFunctionInstruction(StateInstruction):
|
|
|
"""An instruction that runs a generator function."""
|
|
|
- def __init__(self, function, argument_dict):
|
|
|
+ def __init__(self, function, argument_dict, result_type=NO_RESULT_TYPE):
|
|
|
StateInstruction.__init__(self)
|
|
|
self.function = function
|
|
|
self.argument_dict = argument_dict
|
|
|
+ self.result_type_cache = result_type
|
|
|
+
|
|
|
+ def get_opcode(self):
|
|
|
+ """Gets the opcode for this state instruction."""
|
|
|
+ return "TAIL_CALL_KWARGS"
|
|
|
+
|
|
|
+ def get_arguments(self):
|
|
|
+ """Gets this state instruction's argument list."""
|
|
|
+ return [self.function, self.argument_dict]
|
|
|
+
|
|
|
+class RegisterDebugInfoInstruction(StateInstruction):
|
|
|
+ """An instruction that sends a DEBUG_INFO request to the request handler."""
|
|
|
+ def __init__(self, function_name, function_source_map, function_origin):
|
|
|
+ StateInstruction.__init__(self)
|
|
|
+ self.function_name = function_name
|
|
|
+ self.function_source_map = function_source_map
|
|
|
+ self.function_origin = function_origin
|
|
|
|
|
|
def get_result_type_impl(self):
|
|
|
"""Gets the type of value produced by this instruction."""
|
|
@@ -785,11 +1015,11 @@ class RunTailGeneratorFunctionInstruction(StateInstruction):
|
|
|
|
|
|
def get_opcode(self):
|
|
|
"""Gets the opcode for this state instruction."""
|
|
|
- return "TAIL_CALL_KWARGS"
|
|
|
+ return "DEBUG_INFO"
|
|
|
|
|
|
def get_arguments(self):
|
|
|
"""Gets this state instruction's argument list."""
|
|
|
- return [self.function, self.argument_dict]
|
|
|
+ return [self.function_name, self.function_source_map, self.function_origin]
|
|
|
|
|
|
class VariableName(object):
|
|
|
"""A data structure that unifies names across instructions that access the
|
|
@@ -802,6 +1032,12 @@ class VariableName(object):
|
|
|
instruction if it is not None."""
|
|
|
return self.name
|
|
|
|
|
|
+ def __str__(self):
|
|
|
+ return self.name
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return 'VariableName(%r)' % self.name
|
|
|
+
|
|
|
class VariableInstruction(Instruction):
|
|
|
"""A base class for instructions that access variables."""
|
|
|
def __init__(self, name):
|
|
@@ -863,6 +1099,60 @@ class StoreLocalInstruction(LocalInstruction):
|
|
|
The statement is appended immediately to the code generator."""
|
|
|
code_generator.append_move_definition(self, self.value)
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return 'StoreLocalInstruction(%r, %r)' % (self.name, self.value)
|
|
|
+
|
|
|
+class TupleStoreLocalInstruction(VoidInstruction):
|
|
|
+ """Assigns a number of values to a number of variables."""
|
|
|
+ def __init__(self, name_value_pairs):
|
|
|
+ VoidInstruction.__init__(self)
|
|
|
+ self.name_value_pairs = name_value_pairs
|
|
|
+
|
|
|
+ def get_children(self):
|
|
|
+ """Gets this instruction's sequence of child instructions."""
|
|
|
+ return [val for _, val in self.name_value_pairs]
|
|
|
+
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return False
|
|
|
+
|
|
|
+ def create(self, new_children):
|
|
|
+ """Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
+ new_name_value_pairs = [
|
|
|
+ (name, new_value) for (name, _), new_value in zip(self.name_value_pairs, new_children)]
|
|
|
+ return TupleStoreLocalInstruction(new_name_value_pairs)
|
|
|
+
|
|
|
+ def generate_python_def(self, code_generator):
|
|
|
+ """Generates a Python statement that executes this instruction.
|
|
|
+ The statement is appended immediately to the code generator."""
|
|
|
+ tuple_lhs = []
|
|
|
+ tuple_rhs = []
|
|
|
+ for name, value in self.name_value_pairs:
|
|
|
+ if isinstance(name, str) or isinstance(name, unicode) or name is None:
|
|
|
+ variable = VariableName(name)
|
|
|
+ else:
|
|
|
+ variable = name
|
|
|
+
|
|
|
+ # Retrieve the result name for the variable.
|
|
|
+ var_result_name = code_generator.get_result_name(variable)
|
|
|
+ # Encourage the value to take on the same result name as the variable.
|
|
|
+ value_result_name = code_generator.get_result_name(value, var_result_name)
|
|
|
+ if value.has_definition():
|
|
|
+ # Generate the value' definition.
|
|
|
+ value.generate_python_def(code_generator)
|
|
|
+
|
|
|
+ # Only perform an assignment if it's truly necessary.
|
|
|
+ if var_result_name != value_result_name or not value.has_result_temporary():
|
|
|
+ tuple_lhs.append(var_result_name)
|
|
|
+ tuple_rhs.append(value.generate_python_use(code_generator))
|
|
|
+
|
|
|
+ if len(tuple_lhs) > 0:
|
|
|
+ code_generator.append_line(
|
|
|
+ '%s = %s' % (', '.join(tuple_lhs), ', '.join(tuple_rhs)))
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return 'TupleStoreLocalInstruction(%r)' % (self.name_value_pairs)
|
|
|
+
|
|
|
class LoadLocalInstruction(LocalInstruction):
|
|
|
"""An instruction that loads a value from a local variable."""
|
|
|
def has_definition_impl(self):
|
|
@@ -877,6 +1167,9 @@ class LoadLocalInstruction(LocalInstruction):
|
|
|
"""Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
return self
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return 'LoadLocalInstruction(%r)' % self.name
|
|
|
+
|
|
|
class DefineFunctionInstruction(VariableInstruction):
|
|
|
"""An instruction that defines a function."""
|
|
|
def __init__(self, name, parameter_list, body):
|
|
@@ -902,6 +1195,9 @@ class DefineFunctionInstruction(VariableInstruction):
|
|
|
self.body.generate_python_def(code_generator)
|
|
|
code_generator.decrease_indentation()
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return 'DefineFunctionInstruction(%r, %r, %r)' % (self.name, self.parameter_list, self.body)
|
|
|
+
|
|
|
class LocalExistsInstruction(LocalInstruction):
|
|
|
"""An instruction that checks if a local variable exists."""
|
|
|
def has_definition_impl(self):
|
|
@@ -921,6 +1217,9 @@ class LocalExistsInstruction(LocalInstruction):
|
|
|
result. The expression is returned as a string."""
|
|
|
return "'%s' in locals()" % self.get_result_name_override(code_generator)
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return 'LocalExistsInstruction(%r)' % self.name
|
|
|
+
|
|
|
class LoadGlobalInstruction(VariableInstruction):
|
|
|
"""An instruction that loads a value from a global variable."""
|
|
|
def has_definition_impl(self):
|
|
@@ -935,6 +1234,58 @@ class LoadGlobalInstruction(VariableInstruction):
|
|
|
"""Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
return self
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return 'LoadGlobalInstruction(%r)' % self.name
|
|
|
+
|
|
|
+class StoreGlobalInstruction(VariableInstruction):
|
|
|
+ """An instruction that assigns a value to a global variable."""
|
|
|
+ def __init__(self, name, value):
|
|
|
+ VariableInstruction.__init__(self, name)
|
|
|
+ self.value = value
|
|
|
+
|
|
|
+ def get_children(self):
|
|
|
+ """Gets this instruction's sequence of child instructions."""
|
|
|
+ return [self.value]
|
|
|
+
|
|
|
+ def create(self, new_children):
|
|
|
+ """Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
+ val, = new_children
|
|
|
+ return StoreGlobalInstruction(self.name, val)
|
|
|
+
|
|
|
+ def generate_python_def(self, code_generator):
|
|
|
+ """Generates a Python statement that executes this instruction.
|
|
|
+ The statement is appended immediately to the code generator."""
|
|
|
+ code_generator.append_move_definition(self, self.value)
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return 'StoreGlobalInstruction(%r, %r)' % (self.name, self.value)
|
|
|
+
|
|
|
+class DeclareGlobalInstruction(VariableInstruction):
|
|
|
+ """An instruction that declares a name as a global variable."""
|
|
|
+ def get_children(self):
|
|
|
+ """Gets this instruction's sequence of child instructions."""
|
|
|
+ return []
|
|
|
+
|
|
|
+ def get_result_type_impl(self):
|
|
|
+ """Gets the type of value produced by this instruction."""
|
|
|
+ return NO_RESULT_TYPE
|
|
|
+
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return False
|
|
|
+
|
|
|
+ def create(self, new_children):
|
|
|
+ """Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
+ return self
|
|
|
+
|
|
|
+ def generate_python_def(self, code_generator):
|
|
|
+ """Generates a Python statement that executes this instruction.
|
|
|
+ The statement is appended immediately to the code generator."""
|
|
|
+ code_generator.append_line('global %s' % self.name)
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return 'DeclareGlobalInstruction(%r)' % self.name
|
|
|
+
|
|
|
class LoadIndexInstruction(Instruction):
|
|
|
"""An instruction that produces a value by indexing a specified expression with
|
|
|
a given key."""
|
|
@@ -945,12 +1296,16 @@ class LoadIndexInstruction(Instruction):
|
|
|
|
|
|
def has_definition_impl(self):
|
|
|
"""Tells if this instruction requires a definition."""
|
|
|
- return False
|
|
|
+ return self.indexed.has_definition() or self.key.has_definition()
|
|
|
|
|
|
def get_children(self):
|
|
|
"""Gets this instruction's sequence of child instructions."""
|
|
|
return [self.indexed, self.key]
|
|
|
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return False
|
|
|
+
|
|
|
def create(self, new_children):
|
|
|
"""Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
indexed, key = new_children
|
|
@@ -959,15 +1314,25 @@ class LoadIndexInstruction(Instruction):
|
|
|
def generate_python_use(self, code_generator):
|
|
|
"""Generates a Python expression that retrieves this instruction's
|
|
|
result. The expression is returned as a string."""
|
|
|
- if self.indexed.has_definition():
|
|
|
+ return "%s[%s]" % (
|
|
|
+ self.indexed.generate_python_use(code_generator),
|
|
|
+ self.key.generate_python_use(code_generator))
|
|
|
+
|
|
|
+ def generate_python_def(self, code_generator):
|
|
|
+ """Generates a Python statement that executes this instruction.
|
|
|
+ The statement is appended immediately to the code generator."""
|
|
|
+ indexed_has_def, key_has_def = self.indexed.has_definition(), self.key.has_definition()
|
|
|
+ if indexed_has_def:
|
|
|
self.indexed.generate_python_def(code_generator)
|
|
|
|
|
|
- if self.key.has_definition():
|
|
|
+ if key_has_def:
|
|
|
self.key.generate_python_def(code_generator)
|
|
|
|
|
|
- return "%s[%s]" % (
|
|
|
- self.indexed.generate_python_use(code_generator),
|
|
|
- self.key.generate_python_use(code_generator))
|
|
|
+ if not indexed_has_def and not key_has_def:
|
|
|
+ code_generator.append_line('pass')
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return 'LoadIndexInstruction(%r, %r)' % (self.indexed, self.key)
|
|
|
|
|
|
class LoadMemberInstruction(Instruction):
|
|
|
"""An instruction that produces a value by loading a member from a container."""
|
|
@@ -984,6 +1349,10 @@ class LoadMemberInstruction(Instruction):
|
|
|
"""Gets this instruction's sequence of child instructions."""
|
|
|
return [self.container]
|
|
|
|
|
|
+ def has_result_temporary(self):
|
|
|
+ """Tells if this instruction stores its result in a temporary."""
|
|
|
+ return False
|
|
|
+
|
|
|
def create(self, new_children):
|
|
|
"""Creates a new instruction of this type from the given sequence of child instructions."""
|
|
|
container, = new_children
|
|
@@ -1001,6 +1370,9 @@ class LoadMemberInstruction(Instruction):
|
|
|
self.container.generate_python_use(code_generator),
|
|
|
self.member_name)
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return 'LoadMemberInstruction(%r, %r)' % (self.container, self.member_name)
|
|
|
+
|
|
|
class StoreMemberInstruction(VoidInstruction):
|
|
|
"""An instruction that stores a value in a container member."""
|
|
|
def __init__(self, container, member_name, value):
|
|
@@ -1032,6 +1404,9 @@ class StoreMemberInstruction(VoidInstruction):
|
|
|
self.member_name,
|
|
|
self.value.generate_python_use(code_generator)))
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return 'StoreMemberInstruction(%r, %r, %r)' % (self.container, self.member_name, self.value)
|
|
|
+
|
|
|
class NopInstruction(VoidInstruction):
|
|
|
"""A nop instruction, which allows for the kernel's thread of execution to be interrupted."""
|
|
|
def generate_python_def(self, code_generator):
|
|
@@ -1039,6 +1414,9 @@ class NopInstruction(VoidInstruction):
|
|
|
The statement is appended immediately to the code generator."""
|
|
|
code_generator.append_line('yield %s' % repr(NOP_LITERAL))
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ return 'NopInstruction()'
|
|
|
+
|
|
|
class ReadValueInstruction(StateInstruction):
|
|
|
"""An instruction that reads a value from a node."""
|
|
|
def __init__(self, node_id):
|
|
@@ -1079,6 +1457,21 @@ class ReadDictionaryValueInstruction(StateInstruction):
|
|
|
"""Gets this state instruction's argument list."""
|
|
|
return [self.node_id, self.key]
|
|
|
|
|
|
+class ReadDictionaryNodeInstruction(StateInstruction):
|
|
|
+ """An instruction that reads a dictionary node."""
|
|
|
+ def __init__(self, node_id, key):
|
|
|
+ StateInstruction.__init__(self)
|
|
|
+ self.node_id = node_id
|
|
|
+ self.key = key
|
|
|
+
|
|
|
+ def get_opcode(self):
|
|
|
+ """Gets the opcode for this state instruction."""
|
|
|
+ return "RDN"
|
|
|
+
|
|
|
+ def get_arguments(self):
|
|
|
+ """Gets this state instruction's argument list."""
|
|
|
+ return [self.node_id, self.key]
|
|
|
+
|
|
|
class ReadDictionaryEdgeInstruction(StateInstruction):
|
|
|
"""An instruction that reads a dictionary edge."""
|
|
|
def __init__(self, node_id, key):
|
|
@@ -1094,6 +1487,20 @@ class ReadDictionaryEdgeInstruction(StateInstruction):
|
|
|
"""Gets this state instruction's argument list."""
|
|
|
return [self.node_id, self.key]
|
|
|
|
|
|
+class ReadDictionaryKeysInstruction(StateInstruction):
|
|
|
+ """An instruction that reads all keys from a dictionary."""
|
|
|
+ def __init__(self, node_id):
|
|
|
+ StateInstruction.__init__(self)
|
|
|
+ self.node_id = node_id
|
|
|
+
|
|
|
+ def get_opcode(self):
|
|
|
+ """Gets the opcode for this state instruction."""
|
|
|
+ return "RDK"
|
|
|
+
|
|
|
+ def get_arguments(self):
|
|
|
+ """Gets this state instruction's argument list."""
|
|
|
+ return [self.node_id]
|
|
|
+
|
|
|
class ReadEdgeInstruction(StateInstruction):
|
|
|
"""An instruction that reads an edge."""
|
|
|
def __init__(self, node_id):
|
|
@@ -1247,7 +1654,9 @@ def create_block(*statements):
|
|
|
statements[0],
|
|
|
create_block(*statements[1:]))
|
|
|
|
|
|
-def create_jit_call(target, named_arguments, kwargs):
|
|
|
+def create_jit_call(
|
|
|
+ target, named_arguments, kwargs,
|
|
|
+ create_run_generator=RunGeneratorFunctionInstruction):
|
|
|
"""Creates a call that abides by the JIT's calling convention."""
|
|
|
# A JIT call looks like this:
|
|
|
#
|
|
@@ -1273,9 +1682,19 @@ def create_jit_call(target, named_arguments, kwargs):
|
|
|
[kwargs]))
|
|
|
return CompoundInstruction(
|
|
|
create_block(*results),
|
|
|
- RunGeneratorFunctionInstruction(
|
|
|
+ create_run_generator(
|
|
|
target, arg_dict.create_load(), NODE_RESULT_TYPE))
|
|
|
|
|
|
+def evaluate_and_load(value):
|
|
|
+ """Creates a statement that evaluates the given tree, and creates
|
|
|
+ an expression that loads the result. These instructions are returned
|
|
|
+ as a pair of trees."""
|
|
|
+ if isinstance(value, (LoadLocalInstruction, LiteralInstruction)):
|
|
|
+ return EmptyInstruction(), value
|
|
|
+ else:
|
|
|
+ store = StoreLocalInstruction(None, value)
|
|
|
+ return IgnoreInstruction(store), store.create_load()
|
|
|
+
|
|
|
def create_new_local_node(local_variable, connected_node, edge_variable=None):
|
|
|
"""Creates a local node that is the backing storage for a local variable.
|
|
|
This node is connected to a given node to make sure it's not perceived
|
|
@@ -1286,22 +1705,7 @@ def create_new_local_node(local_variable, connected_node, edge_variable=None):
|
|
|
if edge_variable is not None:
|
|
|
create_edge = StoreLocalInstruction(edge_variable, create_edge)
|
|
|
|
|
|
- return create_block(local_store, create_edge)
|
|
|
-
|
|
|
-def with_debug_info_trace(instruction, debug_info, function_name):
|
|
|
- """Prepends the given instruction with a tracing instruction that prints
|
|
|
- the given debug information and function name."""
|
|
|
- if debug_info is None and function_name is None:
|
|
|
- return instruction
|
|
|
- else:
|
|
|
- if debug_info is None:
|
|
|
- debug_info = 'unknown location '
|
|
|
- if function_name is None:
|
|
|
- function_name = 'unknown function'
|
|
|
- return create_block(
|
|
|
- PrintInstruction(
|
|
|
- LiteralInstruction('TRACE: %s(%s, JIT)' % (debug_info, function_name))),
|
|
|
- instruction)
|
|
|
+ return create_block(IgnoreInstruction(local_store), IgnoreInstruction(create_edge))
|
|
|
|
|
|
def map_instruction_tree_top_down(function, instruction):
|
|
|
"""Applies the given mapping function to every instruction in the tree
|
|
@@ -1464,19 +1868,3 @@ def protect_temporaries_from_gc(instruction, connected_node):
|
|
|
return instruction
|
|
|
|
|
|
return map_instruction_tree_top_down(protect_result, instruction)
|
|
|
-
|
|
|
-if __name__ == "__main__":
|
|
|
- example_tree = SelectInstruction(
|
|
|
- LiteralInstruction(True),
|
|
|
- LoopInstruction(
|
|
|
- CompoundInstruction(
|
|
|
- BreakInstruction(),
|
|
|
- CompoundInstruction(
|
|
|
- EmptyInstruction(),
|
|
|
- ContinueInstruction()
|
|
|
- )
|
|
|
- )
|
|
|
- ),
|
|
|
- ReturnInstruction(
|
|
|
- EmptyInstruction()))
|
|
|
- print(example_tree.simplify())
|