/// <summary>
/// Edit the parse branch for a loopfrom statement, rearranging its component
/// parts into a simpler unrolled form.<br/>
/// When given this rule:<br/>
/// <br/>
/// FROM {(init statements)} UNTIL expr STEP {(inc statements)} DO {(body statements)} <br/>
/// <br/>
/// It will edit its own child nodes and transform them into a new parse tree branch as if this had
/// been what was in the source code instead:<br/>
/// <br/>
/// { (init statements) UNTIL expr { (body statements) (inc statements) } }<br/>
/// <br/>
/// Thus any variables declared inside (init statements) are in scope during the body of the loop.<br/>
/// The actual logic of doing an UNTIL loop will fall upon VisitUntilNode to deal with later in the compile.<br/>
/// </summary>
/// <param name="node"></param>
private void RearrangeLoopFromNode(ParseNode node)
{
// Safety check to see if I've already been rearranged into my final form, just in case
// the recursion logic is messed up and this gets called twice on the same node:
if (node.Nodes.Count == 1 && node.Nodes[0].Token.Type == TokenType.instruction_block)
return;
// ReSharper disable RedundantDefaultFieldInitializer
ParseNode initBlock = null;
ParseNode checkExpression = null;
ParseNode untilTokenNode = null;
ParseNode stepBlock = null;
ParseNode doBlock = null;
// ReSharper enable RedundantDefaultFieldInitializer
for( int index = 0 ; index < node.Nodes.Count - 1 ; index += 2 )
{
switch (node.Nodes[index].Token.Type)
{
case TokenType.FROM:
initBlock = node.Nodes[index+1];
break;
case TokenType.UNTIL:
untilTokenNode = node.Nodes[index];
checkExpression = node.Nodes[index+1];
break;
case TokenType.STEP:
stepBlock = node.Nodes[index+1];
break;
case TokenType.DO:
doBlock = node.Nodes[index+1];
break;
// no default because anything else is a syntax error and it won't even get as far as this method in that case.
}
}
// These probably can't happen because the parser would have barfed before it got to this method:
if (initBlock == null)
throw new KOSCompileException("Missing FROM block in FROM loop.");
if (checkExpression == null || untilTokenNode == null)
throw new KOSCompileException("Missing UNTIL check expression in FROM loop.");
if (stepBlock == null)
throw new KOSCompileException("Missing STEP block in FROM loop.");
if (doBlock == null)
throw new KOSCompileException("Missing loop body (DO block) in FROM loop.");
// Append the step instructions to the tail end of the body block's instructions:
foreach (ParseNode child in stepBlock.Nodes)
doBlock.Nodes.Add(child);
// Make a new empty until loop node, which will get added to the init block eventually:
var untilStatementTok = new Token
{
Type = TokenType.until_stmt,
Line = untilTokenNode.Token.Line,
Column = untilTokenNode.Token.Column,
File = untilTokenNode.Token.File
};
ParseNode untilNode = initBlock.CreateNode(untilStatementTok, untilStatementTok.ToString());
// (The direct manipulation of the tree's parent pointers, seen below, is bad form,
// but TinyPg doesn't seem to have given us good primitives to append an existing node to the tree to do it for us.
// CreateNode() makes a brand new empty node attached to the parent, but there seems to be no way to take an
// existing node and attach it elsewhere without directly changing the Parent property as seen in the lines below:)
// Populate that until loop node with the parts from this rule:
untilNode.Nodes.Add(untilTokenNode); untilTokenNode.Parent = untilNode;
untilNode.Nodes.Add(checkExpression); checkExpression.Parent = untilNode;
untilNode.Nodes.Add(doBlock); doBlock.Parent = untilNode;
// And now append that until loop to the tail end of the init block:
initBlock.Nodes.Add(untilNode); // parent already assigned by initBlock.CreateNode() above.
// The init block is now actually the entire loop, having been exploded and unrolled into its
// new form, make that be our only node:
node.Nodes.Clear();
node.Nodes.Add(initBlock); // initBlock's parent already points at node to begin with.
// The FROM loop node is still in the parent's list, but it contains this new rearranged sub-tree
// instead of its original.
}