Overhauled statement eval system so we always get back a list of statements.
This commit is contained in:
parent
b0778e1110
commit
6221d0273b
@ -74,10 +74,10 @@ class ControlFlow:
|
|||||||
|
|
||||||
class ConvertedAction:
|
class ConvertedAction:
|
||||||
# An action that has been analyzed and converted to an intermediate representation.
|
# An action that has been analyzed and converted to an intermediate representation.
|
||||||
semi = False
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MultiStatementAction(ConvertedAction):
|
class MultiAction(ConvertedAction):
|
||||||
# An action that allows us to expand the number of lines we have to work with, for
|
# An action that allows us to expand the number of lines we have to work with, for
|
||||||
# opcodes that perform more than one statement's worth of actions.
|
# opcodes that perform more than one statement's worth of actions.
|
||||||
def __init__(self, actions: Sequence[ConvertedAction]) -> None:
|
def __init__(self, actions: Sequence[ConvertedAction]) -> None:
|
||||||
@ -85,12 +85,13 @@ class MultiStatementAction(ConvertedAction):
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
# We should never emit one of these in printing.
|
# We should never emit one of these in printing.
|
||||||
return f"MultiStatementAction({self.actions})"
|
return f"MultiAction({self.actions})"
|
||||||
|
|
||||||
|
|
||||||
class Statement(ConvertedAction):
|
class Statement(ConvertedAction):
|
||||||
# This is just a type class for finished statements.
|
# This is just a type class for finished statements.
|
||||||
semi = True
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
raise NotImplementedError(f"{self.__class__.__name__} does not implement render()!")
|
||||||
|
|
||||||
|
|
||||||
def object_ref(obj: Any) -> str:
|
def object_ref(obj: Any) -> str:
|
||||||
@ -126,12 +127,18 @@ class BreakStatement(Statement):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
return [f"{prefix}break;"]
|
||||||
|
|
||||||
|
|
||||||
class ContinueStatement(Statement):
|
class ContinueStatement(Statement):
|
||||||
# A continue in a loop (forces execution to the top of the loop).
|
# A continue in a loop (forces execution to the top of the loop).
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "continue"
|
return "continue"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
return [f"{prefix}continue;"]
|
||||||
|
|
||||||
|
|
||||||
class GotoStatement(Statement):
|
class GotoStatement(Statement):
|
||||||
# A goto, including the ID of the chunk we want to jump to.
|
# A goto, including the ID of the chunk we want to jump to.
|
||||||
@ -141,6 +148,9 @@ class GotoStatement(Statement):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"goto label_{self.location}"
|
return f"goto label_{self.location}"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
return [f"{prefix}goto label_{self.location};"]
|
||||||
|
|
||||||
|
|
||||||
class NullReturnStatement(Statement):
|
class NullReturnStatement(Statement):
|
||||||
# A statement which directs the control flow to the end of the code, but
|
# A statement which directs the control flow to the end of the code, but
|
||||||
@ -148,12 +158,19 @@ class NullReturnStatement(Statement):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "return"
|
return "return"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
return [f"{prefix}return;"]
|
||||||
|
|
||||||
|
|
||||||
class NopStatement(Statement):
|
class NopStatement(Statement):
|
||||||
# A literal no-op. We will get rid of these in an optimizing pass.
|
# A literal no-op. We will get rid of these in an optimizing pass.
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "nop"
|
return "nop"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
# We should never render this!
|
||||||
|
raise Exception("Logic error!")
|
||||||
|
|
||||||
|
|
||||||
class ExpressionStatement(Statement):
|
class ExpressionStatement(Statement):
|
||||||
# A statement which is an expression that discards its return.
|
# A statement which is an expression that discards its return.
|
||||||
@ -163,18 +180,27 @@ class ExpressionStatement(Statement):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"{self.expr.render()}"
|
return f"{self.expr.render()}"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
return [f"{prefix}{self.expr.render()};"]
|
||||||
|
|
||||||
|
|
||||||
class StopMovieStatement(Statement):
|
class StopMovieStatement(Statement):
|
||||||
# Stop the movie, this is an actionscript-specific opcode.
|
# Stop the movie, this is an actionscript-specific opcode.
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "builtin_StopPlaying()"
|
return "builtin_StopPlaying()"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
return [f"{prefix}builtin_StopPlaying();"]
|
||||||
|
|
||||||
|
|
||||||
class PlayMovieStatement(Statement):
|
class PlayMovieStatement(Statement):
|
||||||
# Play the movie, this is an actionscript-specific opcode.
|
# Play the movie, this is an actionscript-specific opcode.
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "builtin_StartPlaying()"
|
return "builtin_StartPlaying()"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
return [f"{prefix}builtin_StartPlaying();"]
|
||||||
|
|
||||||
|
|
||||||
class FunctionCall(Expression):
|
class FunctionCall(Expression):
|
||||||
# Call a method on an object.
|
# Call a method on an object.
|
||||||
@ -258,6 +284,12 @@ class SetMemberStatement(Statement):
|
|||||||
val = value_ref(self.valueref)
|
val = value_ref(self.valueref)
|
||||||
return f"{ref}.{name} = {val}"
|
return f"{ref}.{name} = {val}"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
ref = object_ref(self.objectref)
|
||||||
|
name = name_ref(self.name)
|
||||||
|
val = value_ref(self.valueref)
|
||||||
|
return [f"{prefix}{ref}.{name} = {val};"]
|
||||||
|
|
||||||
|
|
||||||
class DeleteVariableStatement(Statement):
|
class DeleteVariableStatement(Statement):
|
||||||
# Call a method on an object.
|
# Call a method on an object.
|
||||||
@ -268,6 +300,10 @@ class DeleteVariableStatement(Statement):
|
|||||||
name = name_ref(self.name)
|
name = name_ref(self.name)
|
||||||
return f"del {name}"
|
return f"del {name}"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
name = name_ref(self.name)
|
||||||
|
return [f"{prefix}delete {name};"]
|
||||||
|
|
||||||
|
|
||||||
class StoreRegisterStatement(Statement):
|
class StoreRegisterStatement(Statement):
|
||||||
# Set a variable to a value.
|
# Set a variable to a value.
|
||||||
@ -279,6 +315,10 @@ class StoreRegisterStatement(Statement):
|
|||||||
val = value_ref(self.valueref)
|
val = value_ref(self.valueref)
|
||||||
return f"{self.register} = {val}"
|
return f"{self.register} = {val}"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
val = value_ref(self.valueref)
|
||||||
|
return [f"{prefix}{self.register} = {val};"]
|
||||||
|
|
||||||
|
|
||||||
class SetVariableStatement(Statement):
|
class SetVariableStatement(Statement):
|
||||||
# Set a variable to a value.
|
# Set a variable to a value.
|
||||||
@ -291,6 +331,11 @@ class SetVariableStatement(Statement):
|
|||||||
val = value_ref(self.valueref)
|
val = value_ref(self.valueref)
|
||||||
return f"{name} = {val}"
|
return f"{name} = {val}"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
name = name_ref(self.name)
|
||||||
|
val = value_ref(self.valueref)
|
||||||
|
return [f"{prefix}{name} = {val};"]
|
||||||
|
|
||||||
|
|
||||||
class SetLocalStatement(Statement):
|
class SetLocalStatement(Statement):
|
||||||
# Define a local variable with a value.
|
# Define a local variable with a value.
|
||||||
@ -303,6 +348,11 @@ class SetLocalStatement(Statement):
|
|||||||
val = value_ref(self.valueref)
|
val = value_ref(self.valueref)
|
||||||
return f"local {name} = {val}"
|
return f"local {name} = {val}"
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
name = name_ref(self.name)
|
||||||
|
val = value_ref(self.valueref)
|
||||||
|
return [f"{prefix}local {name} = {val};"]
|
||||||
|
|
||||||
|
|
||||||
class IfExpr(ConvertedAction):
|
class IfExpr(ConvertedAction):
|
||||||
# This is just for typing.
|
# This is just for typing.
|
||||||
@ -310,9 +360,6 @@ class IfExpr(ConvertedAction):
|
|||||||
|
|
||||||
|
|
||||||
class IsUndefinedIf(IfExpr):
|
class IsUndefinedIf(IfExpr):
|
||||||
# No semicolon on this.
|
|
||||||
semi = False
|
|
||||||
|
|
||||||
def __init__(self, conditional: Any, negate: bool) -> None:
|
def __init__(self, conditional: Any, negate: bool) -> None:
|
||||||
self.conditional = conditional
|
self.conditional = conditional
|
||||||
self.negate = negate
|
self.negate = negate
|
||||||
@ -326,9 +373,6 @@ class IsUndefinedIf(IfExpr):
|
|||||||
|
|
||||||
|
|
||||||
class IsBooleanIf(IfExpr):
|
class IsBooleanIf(IfExpr):
|
||||||
# No semicolon on this.
|
|
||||||
semi = False
|
|
||||||
|
|
||||||
def __init__(self, conditional: Any, negate: bool) -> None:
|
def __init__(self, conditional: Any, negate: bool) -> None:
|
||||||
self.conditional = conditional
|
self.conditional = conditional
|
||||||
self.negate = negate
|
self.negate = negate
|
||||||
@ -342,9 +386,6 @@ class IsBooleanIf(IfExpr):
|
|||||||
|
|
||||||
|
|
||||||
class IsEqualIf(IfExpr):
|
class IsEqualIf(IfExpr):
|
||||||
# No semicolon on this.
|
|
||||||
semi = False
|
|
||||||
|
|
||||||
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
||||||
self.conditional1 = conditional1
|
self.conditional1 = conditional1
|
||||||
self.conditional2 = conditional2
|
self.conditional2 = conditional2
|
||||||
@ -357,9 +398,6 @@ class IsEqualIf(IfExpr):
|
|||||||
|
|
||||||
|
|
||||||
class IsStrictEqualIf(IfExpr):
|
class IsStrictEqualIf(IfExpr):
|
||||||
# No semicolon on this.
|
|
||||||
semi = False
|
|
||||||
|
|
||||||
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
||||||
self.conditional1 = conditional1
|
self.conditional1 = conditional1
|
||||||
self.conditional2 = conditional2
|
self.conditional2 = conditional2
|
||||||
@ -372,9 +410,6 @@ class IsStrictEqualIf(IfExpr):
|
|||||||
|
|
||||||
|
|
||||||
class MagnitudeIf(IfExpr):
|
class MagnitudeIf(IfExpr):
|
||||||
# No semicolon on this.
|
|
||||||
semi = False
|
|
||||||
|
|
||||||
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
||||||
self.conditional1 = conditional1
|
self.conditional1 = conditional1
|
||||||
self.conditional2 = conditional2
|
self.conditional2 = conditional2
|
||||||
@ -387,9 +422,6 @@ class MagnitudeIf(IfExpr):
|
|||||||
|
|
||||||
|
|
||||||
class MagnitudeEqualIf(IfExpr):
|
class MagnitudeEqualIf(IfExpr):
|
||||||
# No semicolon on this.
|
|
||||||
semi = False
|
|
||||||
|
|
||||||
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
||||||
self.conditional1 = conditional1
|
self.conditional1 = conditional1
|
||||||
self.conditional2 = conditional2
|
self.conditional2 = conditional2
|
||||||
@ -402,9 +434,6 @@ class MagnitudeEqualIf(IfExpr):
|
|||||||
|
|
||||||
|
|
||||||
class IfStatement(Statement):
|
class IfStatement(Statement):
|
||||||
# If statements aren't semicoloned.
|
|
||||||
semi = False
|
|
||||||
|
|
||||||
def __init__(self, cond: IfExpr, true_statements: Sequence[Statement], false_statements: Sequence[Statement]) -> None:
|
def __init__(self, cond: IfExpr, true_statements: Sequence[Statement], false_statements: Sequence[Statement]) -> None:
|
||||||
self.cond = cond
|
self.cond = cond
|
||||||
self.true_statements = true_statements
|
self.true_statements = true_statements
|
||||||
@ -413,11 +442,11 @@ class IfStatement(Statement):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
true_entries: List[str] = []
|
true_entries: List[str] = []
|
||||||
for statement in self.true_statements:
|
for statement in self.true_statements:
|
||||||
true_entries.extend([f" {s}" for s in (str(statement) + (';' if statement.semi else '')).split(os.linesep)])
|
true_entries.extend([f" {s}" for s in str(statement).split(os.linesep)])
|
||||||
|
|
||||||
false_entries: List[str] = []
|
false_entries: List[str] = []
|
||||||
for statement in self.false_statements:
|
for statement in self.false_statements:
|
||||||
false_entries.extend([f" {s}" for s in (str(statement) + (';' if statement.semi else '')).split(os.linesep)])
|
false_entries.extend([f" {s}" for s in str(statement).split(os.linesep)])
|
||||||
|
|
||||||
if false_entries:
|
if false_entries:
|
||||||
return os.linesep.join([
|
return os.linesep.join([
|
||||||
@ -434,6 +463,62 @@ class IfStatement(Statement):
|
|||||||
"}"
|
"}"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
true_entries: List[str] = []
|
||||||
|
for statement in self.true_statements:
|
||||||
|
true_entries.extend(statement.render(prefix=prefix + " "))
|
||||||
|
|
||||||
|
false_entries: List[str] = []
|
||||||
|
for statement in self.false_statements:
|
||||||
|
false_entries.extend(statement.render(prefix=prefix + " "))
|
||||||
|
|
||||||
|
if false_entries:
|
||||||
|
return [
|
||||||
|
f"{prefix}{self.cond}",
|
||||||
|
f"{prefix}{{",
|
||||||
|
*true_entries,
|
||||||
|
f"{prefix}}}",
|
||||||
|
f"{prefix}else",
|
||||||
|
f"{prefix}{{",
|
||||||
|
*false_entries,
|
||||||
|
f"{prefix}}}"
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
return [
|
||||||
|
f"{prefix}{self.cond}",
|
||||||
|
f"{prefix}{{",
|
||||||
|
*true_entries,
|
||||||
|
f"{prefix}}}"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DoWhileStatement(Statement):
|
||||||
|
def __init__(self, body: Sequence[Statement]) -> None:
|
||||||
|
self.body = body
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
entries: List[str] = []
|
||||||
|
for statement in self.body:
|
||||||
|
entries.extend([f" {s}" for s in str(statement).split(os.linesep)])
|
||||||
|
|
||||||
|
return os.linesep.join([
|
||||||
|
"do {",
|
||||||
|
os.linesep.join(entries),
|
||||||
|
"} while(True);"
|
||||||
|
])
|
||||||
|
|
||||||
|
def render(self, prefix: str) -> List[str]:
|
||||||
|
entries: List[str] = []
|
||||||
|
for statement in self.body:
|
||||||
|
entries.extend(statement.render(prefix=prefix + " "))
|
||||||
|
|
||||||
|
return [
|
||||||
|
f"{prefix}do",
|
||||||
|
f"{prefix}{{",
|
||||||
|
*entries,
|
||||||
|
f"{prefix}}} while(True);",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class IntermediateIf(ConvertedAction):
|
class IntermediateIf(ConvertedAction):
|
||||||
def __init__(self, parent_action: IfAction, true_statements: Sequence[Statement], false_statements: Sequence[Statement], negate: bool) -> None:
|
def __init__(self, parent_action: IfAction, true_statements: Sequence[Statement], false_statements: Sequence[Statement], negate: bool) -> None:
|
||||||
@ -1388,7 +1473,7 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
# Return the tree, stripped of all dead code (most likely just the return sentinel).
|
# Return the tree, stripped of all dead code (most likely just the return sentinel).
|
||||||
return new_chunks
|
return new_chunks
|
||||||
|
|
||||||
def __eval_stack(self, chunk: ByteCodeChunk, offset_map: Dict[int, int]) -> None:
|
def __eval_stack(self, chunk: ByteCodeChunk, offset_map: Dict[int, int]) -> List[ConvertedAction]:
|
||||||
stack: List[Any] = []
|
stack: List[Any] = []
|
||||||
|
|
||||||
def make_if_expr(action: IfAction, negate: bool) -> IfExpr:
|
def make_if_expr(action: IfAction, negate: bool) -> IfExpr:
|
||||||
@ -1448,7 +1533,7 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
for reg in action.registers:
|
for reg in action.registers:
|
||||||
store_actions.append(StoreRegisterStatement(reg, set_value))
|
store_actions.append(StoreRegisterStatement(reg, set_value))
|
||||||
|
|
||||||
chunk.actions[i] = MultiStatementAction(store_actions)
|
chunk.actions[i] = MultiAction(store_actions)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isinstance(action, JumpAction):
|
if isinstance(action, JumpAction):
|
||||||
@ -1606,8 +1691,11 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
raise Exception(f"TODO: {action}")
|
raise Exception(f"TODO: {action}")
|
||||||
|
|
||||||
# Now, clean up code generation.
|
# Now, clean up code generation.
|
||||||
new_actions: List[ArbitraryOpcode] = []
|
new_actions: List[ConvertedAction] = []
|
||||||
for action in chunk.actions:
|
for action in chunk.actions:
|
||||||
|
if not isinstance(action, ConvertedAction):
|
||||||
|
# We should have handled all AP2Actions at this point!
|
||||||
|
raise Exception("Logic error!")
|
||||||
if isinstance(action, NopStatement):
|
if isinstance(action, NopStatement):
|
||||||
# Filter out noops.
|
# Filter out noops.
|
||||||
continue
|
continue
|
||||||
@ -1615,15 +1703,16 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
if new_actions and isinstance(new_actions[-1], NullReturnStatement):
|
if new_actions and isinstance(new_actions[-1], NullReturnStatement):
|
||||||
# Filter out redundant return statements.
|
# Filter out redundant return statements.
|
||||||
continue
|
continue
|
||||||
if isinstance(action, MultiStatementAction):
|
if isinstance(action, MultiAction):
|
||||||
for new_action in action.actions:
|
for new_action in action.actions:
|
||||||
new_actions.append(new_action)
|
new_actions.append(new_action)
|
||||||
|
|
||||||
new_actions.append(action)
|
new_actions.append(action)
|
||||||
chunk.actions = new_actions
|
return new_actions
|
||||||
|
|
||||||
def __eval_chunks(self, start_id: int, chunks: Sequence[ArbitraryCodeChunk], offset_map: Dict[int, int]) -> None:
|
def __eval_chunks(self, start_id: int, chunks: Sequence[ArbitraryCodeChunk], offset_map: Dict[int, int]) -> List[Statement]:
|
||||||
chunks_by_id: Dict[int, ArbitraryCodeChunk] = {chunk.id: chunk for chunk in chunks}
|
chunks_by_id: Dict[int, ArbitraryCodeChunk] = {chunk.id: chunk for chunk in chunks}
|
||||||
|
statements: List[Statement] = []
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# Grab the chunk to operate on.
|
# Grab the chunk to operate on.
|
||||||
@ -1632,19 +1721,53 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
if isinstance(chunk, Loop):
|
if isinstance(chunk, Loop):
|
||||||
# Evaluate the loop
|
# Evaluate the loop
|
||||||
self.vprint(f"Evaluating graph in Loop {chunk.id}")
|
self.vprint(f"Evaluating graph in Loop {chunk.id}")
|
||||||
self.__eval_chunks(chunk.id, chunk.chunks, offset_map)
|
statements.append(
|
||||||
|
DoWhileStatement(self.__eval_chunks(chunk.id, chunk.chunks, offset_map))
|
||||||
|
)
|
||||||
elif isinstance(chunk, IfBody):
|
elif isinstance(chunk, IfBody):
|
||||||
# Evaluate the if body
|
# We should have evaluated this earlier!
|
||||||
if chunk.true_chunks:
|
raise Exception("Logic error!")
|
||||||
self.vprint(f"Evaluating graph of IfBody {chunk.id} true case")
|
|
||||||
true_start = self.__get_entry_block(chunk.true_chunks)
|
|
||||||
self.__eval_chunks(true_start, chunk.true_chunks, offset_map)
|
|
||||||
if chunk.false_chunks:
|
|
||||||
self.vprint(f"Evaluating graph of IfBody {chunk.id} false case")
|
|
||||||
false_start = self.__get_entry_block(chunk.false_chunks)
|
|
||||||
self.__eval_chunks(false_start, chunk.false_chunks, offset_map)
|
|
||||||
else:
|
else:
|
||||||
self.__eval_stack(chunk, offset_map)
|
new_statements = self.__eval_stack(chunk, offset_map)
|
||||||
|
|
||||||
|
# We need to check and see if the last entry is an IfExpr, and hoist it
|
||||||
|
# into a statement here.
|
||||||
|
if isinstance(new_statements[-1], IfExpr):
|
||||||
|
if_body = chunk.next_chunks[0]
|
||||||
|
if_body_chunk = chunks_by_id[if_body]
|
||||||
|
|
||||||
|
if not isinstance(if_body_chunk, IfBody):
|
||||||
|
# IfBody should always follow a chunk that ends with an if.
|
||||||
|
raise Exception("Logic error!")
|
||||||
|
|
||||||
|
# Evaluate the if body
|
||||||
|
true_statements: List[Statement] = []
|
||||||
|
if if_body_chunk.true_chunks:
|
||||||
|
self.vprint(f"Evaluating graph of IfBody {if_body_chunk.id} true case")
|
||||||
|
true_start = self.__get_entry_block(if_body_chunk.true_chunks)
|
||||||
|
true_statements = self.__eval_chunks(true_start, if_body_chunk.true_chunks, offset_map)
|
||||||
|
false_statements: List[Statement] = []
|
||||||
|
if if_body_chunk.false_chunks:
|
||||||
|
self.vprint(f"Evaluating graph of IfBody {if_body_chunk.id} false case")
|
||||||
|
false_start = self.__get_entry_block(if_body_chunk.false_chunks)
|
||||||
|
false_statements = self.__eval_chunks(false_start, if_body_chunk.false_chunks, offset_map)
|
||||||
|
|
||||||
|
# Convert this IfExpr to a full-blown IfStatement.
|
||||||
|
new_statements[-1] = IfStatement(
|
||||||
|
new_statements[-1],
|
||||||
|
true_statements,
|
||||||
|
false_statements,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Skip evaluating the IfBody next iteration.
|
||||||
|
chunk = if_body_chunk
|
||||||
|
|
||||||
|
# Verify that we converted all the statements properly.
|
||||||
|
for statement in new_statements:
|
||||||
|
if not isinstance(statement, Statement):
|
||||||
|
# We didn't convert a statement properly.
|
||||||
|
raise Exception("Logic error!")
|
||||||
|
statements.append(statement)
|
||||||
|
|
||||||
# Go to the next chunk
|
# Go to the next chunk
|
||||||
if not chunk.next_chunks:
|
if not chunk.next_chunks:
|
||||||
@ -1654,47 +1777,15 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
raise Exception("Logic error!")
|
raise Exception("Logic error!")
|
||||||
start_id = chunk.next_chunks[0]
|
start_id = chunk.next_chunks[0]
|
||||||
|
|
||||||
def __pretty_print(self, start_id: int, chunks: Sequence[ArbitraryCodeChunk], prefix: str = "") -> List[str]:
|
return statements
|
||||||
chunks_by_id: Dict[int, ArbitraryCodeChunk] = {chunk.id: chunk for chunk in chunks}
|
|
||||||
|
def __pretty_print(self, start_id: int, statements: Sequence[Statement], prefix: str = "") -> str:
|
||||||
output: List[str] = []
|
output: List[str] = []
|
||||||
|
|
||||||
while True:
|
for statement in statements:
|
||||||
# Grab the chunk to operate on.
|
output.extend(statement.render(prefix))
|
||||||
chunk = chunks_by_id[start_id]
|
|
||||||
|
|
||||||
if isinstance(chunk, Loop):
|
return os.linesep.join(output)
|
||||||
raise Exception("TODO")
|
|
||||||
elif isinstance(chunk, IfBody):
|
|
||||||
if chunk.true_chunks:
|
|
||||||
output.append(f"{prefix}{{")
|
|
||||||
true_start = self.__get_entry_block(chunk.true_chunks)
|
|
||||||
output.extend(self.__pretty_print(true_start, chunk.true_chunks, prefix=f"{prefix} "))
|
|
||||||
output.append(f"{prefix}}}")
|
|
||||||
else:
|
|
||||||
raise Exception("Logic error!")
|
|
||||||
|
|
||||||
if chunk.false_chunks:
|
|
||||||
output.append(f"{prefix}else")
|
|
||||||
output.append(f"{prefix}{{")
|
|
||||||
false_start = self.__get_entry_block(chunk.false_chunks)
|
|
||||||
output.extend(self.__pretty_print(false_start, chunk.false_chunks, prefix=f"{prefix} "))
|
|
||||||
output.append(f"{prefix}}}")
|
|
||||||
else:
|
|
||||||
for action in chunk.actions:
|
|
||||||
if isinstance(action, ConvertedAction):
|
|
||||||
output.append(f"{prefix}{action}{';' if action.semi else ''}")
|
|
||||||
else:
|
|
||||||
output.append(f"{prefix}UNCONVERTED: {action}")
|
|
||||||
|
|
||||||
# Go to the next chunk
|
|
||||||
if not chunk.next_chunks:
|
|
||||||
break
|
|
||||||
if len(chunk.next_chunks) != 1:
|
|
||||||
# We've checked so this should be impossible.
|
|
||||||
raise Exception("Logic error!")
|
|
||||||
start_id = chunk.next_chunks[0]
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
def __decompile(self) -> str:
|
def __decompile(self) -> str:
|
||||||
# First, we need to construct a control flow graph.
|
# First, we need to construct a control flow graph.
|
||||||
@ -1726,14 +1817,10 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
chunks_loops_and_ifs = self.__check_graph(start_id, chunks_loops_and_ifs)
|
chunks_loops_and_ifs = self.__check_graph(start_id, chunks_loops_and_ifs)
|
||||||
|
|
||||||
# Now, its safe to start actually evaluating the stack.
|
# Now, its safe to start actually evaluating the stack.
|
||||||
self.__eval_chunks(start_id, chunks_loops_and_ifs, offset_map)
|
statements = self.__eval_chunks(start_id, chunks_loops_and_ifs, offset_map)
|
||||||
|
|
||||||
# Finally, let's print the code!
|
# Finally, let's print the code!
|
||||||
if self.main:
|
code = self.__pretty_print(start_id, statements, prefix=" " if self.main else "")
|
||||||
prefix = " "
|
|
||||||
else:
|
|
||||||
prefix = ""
|
|
||||||
code = os.linesep.join(self.__pretty_print(start_id, chunks_loops_and_ifs, prefix=prefix))
|
|
||||||
|
|
||||||
if self.main:
|
if self.main:
|
||||||
code = f"void main(){os.linesep}{{{os.linesep}{code}{os.linesep}}}"
|
code = f"void main(){os.linesep}{{{os.linesep}{code}{os.linesep}}}"
|
||||||
|
@ -618,7 +618,7 @@ class Expression:
|
|||||||
# Any thing that can be evaluated for a result, such as a variable
|
# Any thing that can be evaluated for a result, such as a variable
|
||||||
# reference, function call, or mathematical operation.
|
# reference, function call, or mathematical operation.
|
||||||
def render(self, nested: bool = False) -> str:
|
def render(self, nested: bool = False) -> str:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError(f"{self.__class__.__name__} does not implement render()!")
|
||||||
|
|
||||||
|
|
||||||
# A bunch of stuff for implementing PushAction
|
# A bunch of stuff for implementing PushAction
|
||||||
|
Loading…
Reference in New Issue
Block a user