private SimanticsBlock FindSwitchBlockStart(SimanticsBlock end) { while (end.Parent != null) { if (end.BlockType == SimanticsBlockType.Switch) { return(end); } end = end.Parent; } return(null); }
private void OutputBlock(SimanticsBlock block, CSTranslationClass cls) { for (int i = 0; i < block.IBody.Count - 1; i++) { var inst = block.IBody[i]; var body = cls.Instructions[inst.Index]; switch (body.ReturnType) { case PrimitiveReturnType.SimanticsTrue: case PrimitiveReturnType.SimanticsTrueFalse: //truefalse result discarded, same destination WriteLines(body.Body, true); break; case PrimitiveReturnType.NativeStatementTrue: case PrimitiveReturnType.NativeStatementTrueFalse: //truefalse result discarded, same destination WriteLines(body.Body, false); break; case PrimitiveReturnType.NativeExpressionTrueFalse: //we have a problem... the expression needs to evaluate, but we don't do anything with the result. //put it in the boolean temp and ignore it. if (body.Body.Count == 1) { WriteLine($"_bResult = {body.Body.First()};"); } else { WriteLines(body.Body, true); } break; case PrimitiveReturnType.SimanticsSubroutine: //a subroutine that yields but doesn't branch. default: throw new Exception("Non-statement in the middle of a sequence (detected areas with no branching)?"); } } var lastInst = block.IBody[block.IBody.Count - 1]; var lastBody = cls.Instructions[lastInst.Index]; LastInstruction = block.LastInstructionIndex; string caseConst = null; if (block.BlockType == SimanticsBlockType.Switch || block.BlockType == SimanticsBlockType.SwitchCase || block.BlockType == SimanticsBlockType.SwitchLast) { caseConst = CSScopeMemory.GetConstant(Context, block.SwitchOperand.RhsOwner, block.SwitchOperand.RhsData); } switch (block.BlockType) { case SimanticsBlockType.InstructionSequence: case SimanticsBlockType.IfElse: //just set the instruction based on the value and return back to the while loop if (lastBody.ReturnType == PrimitiveReturnType.SimanticsSubroutine || lastInst.Yields) { UpdateInstruction(); if (lastBody.Body.Count > 1) { WriteLines(lastBody.Body, false); WriteLine($"return _sResult;"); } else { WriteLine($"return {lastBody.Body.First()};"); } break; } switch (lastBody.ReturnType) { case PrimitiveReturnType.NativeStatementTrueFalse: if (lastInst.Instruction.TruePointer == lastInst.Instruction.FalsePointer) { goto case PrimitiveReturnType.NativeStatementTrue; } WriteLines(lastBody.Body, false); WriteLine("if (_bResult) "); OutputBinaryBranch(lastInst.Instruction, cls); break; case PrimitiveReturnType.NativeExpressionTrueFalse: if (lastInst.Instruction.TruePointer == lastInst.Instruction.FalsePointer) { WriteLine($"_bResult = {lastBody.Body.First()}; //true and false do the same thing"); OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); } else { WriteLine($"if ({lastBody.Body.First()}) "); OutputBinaryBranch(lastInst.Instruction, cls); } break; case PrimitiveReturnType.SimanticsTrueFalse: if (lastInst.Instruction.TruePointer == lastInst.Instruction.FalsePointer) { goto case PrimitiveReturnType.SimanticsTrue; } WriteLine($"if ({lastBody.Body.First()} == VMPrimitiveExitCode.GOTO_TRUE) "); OutputBinaryBranch(lastInst.Instruction, cls); break; case PrimitiveReturnType.SimanticsStatement: if (lastInst.Instruction.TruePointer == lastInst.Instruction.FalsePointer) { WriteLines(lastBody.Body, false); OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); } else { WriteLines(lastBody.Body, false); WriteLine($"if (_sResult == VMPrimitiveExitCode.GOTO_TRUE) "); OutputBinaryBranch(lastInst.Instruction, cls); } break; case PrimitiveReturnType.NativeStatementTrue: WriteLines(lastBody.Body, false); OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); break; case PrimitiveReturnType.SimanticsTrue: WriteLines(lastBody.Body, true); OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); break; } if (block.Parent == cls.Structure.RootBlock && cls.HasGlobalSwitch) { WriteLine("break;"); } break; case SimanticsBlockType.Switch: string switchLhs = CSScopeMemory.GetExpression(Context, block.SwitchOperand.LhsOwner, block.SwitchOperand.LhsData, true); WriteLine($"switch ({switchLhs}) "); WriteLine("{"); IndentLevel++; WriteLine($"case {caseConst}:"); IndentLevel++; var set = new HashSet <string> { caseConst }; cls.SwitchPreviousCases[block] = set; OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); WriteLine($"break;"); IndentLevel--; OutputReturnBlockOrJump(lastInst.Instruction.FalsePointer, cls); break; case SimanticsBlockType.SwitchCase: var cset = cls.SwitchPreviousCases[FindSwitchBlockStart(block)]; if (cset.Contains(caseConst)) { WriteLine($"// case {caseConst} duplicated! skipped block at {lastInst.Instruction.FalsePointer}"); } else { cset.Add(caseConst); WriteLine($"case {caseConst}:"); IndentLevel++; OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); WriteLine($"break;"); IndentLevel--; } OutputReturnBlockOrJump(lastInst.Instruction.FalsePointer, cls); break; case SimanticsBlockType.SwitchLast: var lset = cls.SwitchPreviousCases[FindSwitchBlockStart(block)]; if (lset.Contains(caseConst)) { WriteLine($"// case {caseConst} duplicated! skipped block at {lastInst.Instruction.FalsePointer}"); } else { lset.Add(caseConst); WriteLine($"case {caseConst}:"); IndentLevel++; OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); WriteLine($"break;"); IndentLevel--; } WriteLine($"default:"); IndentLevel++; OutputReturnBlockOrJump(lastInst.Instruction.FalsePointer, cls); WriteLine($"break;"); IndentLevel--; IndentLevel--; WriteLine("}"); if (FindSwitchBlockStart(block).Parent == cls.Structure.RootBlock && cls.HasGlobalSwitch) { WriteLine("break;"); } break; default: throw new Exception($"Block type {block.BlockType.ToString()} not yet implemented."); } //our last instruction }