private void AddJumpToEntryPoint(CodePart linkedObject) { if (linkedObject.MainCode.Count <= 0) { return; } var jumpOpcode = new OpcodeBranchJump(); jumpOpcode.DestinationLabel = GetEntryPointLabel(linkedObject); linkedObject.FunctionsCode.Insert(0, jumpOpcode); }
/// <summary> /// Handles the short-circuit logic of boolean OR and boolean AND /// chains. It is like VisitExpressionChain (see elsewhere) but /// in this case it has the special logic to short circuit and skip /// executing the righthand expression if it can. (The generic VisitExpressionXhain /// always evaluates both the left and right sides of the operator first, then /// does the operation). /// </summary> /// <param name="node"></param> private void VisitShortCircuitBoolean(ParseNode node) { NodeStartHousekeeping(node); if (node.Nodes.Count > 1) { // it should always be odd, two arguments and one operator if ((node.Nodes.Count % 2) != 1) return; // Determine if this is a chain of ANDs or a chain or ORs. The parser will // never mix ANDs and ORs into the same ParseNode level. We are guaranteed // that all the operators in this chain match the first operator in the chain: // That guarantee is important. Without it, we can't do short-circuiting like this // because you can't short-circuit a mix of AND and OR at the same precedence. TokenType operation = node.Nodes[1].Token.Type; // Guaranteed to be either TokenType.AND or TokenType.OR // For remembering the instruction pointers from which short-circuit branch jumps came: List<int> shortCircuitFromIndeces = new List<int>(); int nodeIndex = 0; while (nodeIndex < node.Nodes.Count) { if (nodeIndex > 0) // After each term, insert the branch test (which consumes the expr from the stack regardless of if it branches): { shortCircuitFromIndeces.Add(currentCodeSection.Count()); if (operation == TokenType.AND) AddOpcode(new OpcodeBranchIfFalse()); else if (operation == TokenType.OR) AddOpcode(new OpcodeBranchIfTrue()); else throw new KOSException("Assertion check: Broken kerboscript compiler (VisitShortCircuitBoolean). See kOS devs"); } VisitNode(node.Nodes[nodeIndex]); // pushes the next term onto the stack. nodeIndex += 2; // Skip the operator, moving to the next term over. } // If it gets to the end of all that and it still hasn't aborted, then the whole expression's // Boolean value is just the value of its lastmost term, that's already gotten pushed atop the stack. // Leave the lastmost term there, and just skip ahead past the short-circuit landing target: OpcodeBranchJump skipShortCircuitTarget = new OpcodeBranchJump(); skipShortCircuitTarget.Distance = 2; // Hardcoded +2 jump distance skips the upcoming OpcodePush and just lands on // whatever comes next after this VisitNode. Avoids using DestinationLabel // for later relocation because it would be messy to reassign this label later // in whatever VisitNode happens to come up next, when that could be anything. AddOpcode(skipShortCircuitTarget); // Build the instruction all the short circuit checks will jump to if aborting partway through. // (AND's abort when they're false. OR's abort when they're true.) AddOpcode(operation == TokenType.AND ? new OpcodePush(false) : new OpcodePush(true)); string shortCircuitTargetLabel = currentCodeSection[currentCodeSection.Count()-1].Label; // Retroactively re-assign the jump labels of all the short circuit branch operations: foreach (int index in shortCircuitFromIndeces) { currentCodeSection[index].DestinationLabel = shortCircuitTargetLabel; } } else if (node.Nodes.Count == 1) { VisitNode(node.Nodes[0]); // This ParseNode isn't *realy* an expression of AND or OR operators, because // the regex chain of "zero or more" righthand terms.. had zero such terms. // So just delve in deeper to compile whatever part of speech it is further down. } }