public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); Debug.Assert(parent.ChildCount > 0, "No value found for the left hand side of operator: " + token); // Take the previous statement and add it as a sub statement of the newly created operator - we will check for validity in the Compile function parent.MoveChildAtIndex(parent.ChildCount - 1, this); // Add this operator to the tree parent.Add(this); // Now check that there is another element after our operator that we can act on Debug.Assert(tokens.Count > 0, "No value found for the right hand side of operator: " + token); // Get the next token that appears on the right hand side of this operator string rhsOfOperatorToken = CelesteCompiler.PopToken(); // Parse the next token which we will act on if (CelesteCompiler.CompileToken(rhsOfOperatorToken, parent)) { // Take the value that has been added to the root and add it under this operator instead parent.MoveChildAtIndex(parent.ChildCount - 1, this); } else { // Error message if we cannot parse the next token Debug.Fail("Could not compile token: " + token + " in operator " + token); } }
/// <summary> /// Call compile when you wish to implement the function. /// Process the actual variable names that have been passed into the function using the token. /// Create a variable underneath this for each variable we are passing in. /// </summary> /// <param name="parent"></param> /// <param name="token"></param> /// <param name="tokens"></param> /// <param name="lines"></param> public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { // We have invoked this function because we have parentheses if (tokens.Count > 0 && tokens.First.Value == OpenParenthesis.scriptToken) { // Group our inputs under this object so that we can store them underneath this function CompiledStatement thisCallsParams = new CompiledStatement(); if (ParameterNames.Count > 0) { // Only add our parameters if we have any. // Otherwise this will just strip away the start and end parentheses Add(thisCallsParams); } string openingParenthesis = CelesteCompiler.PopToken(); Debug.Assert(openingParenthesis == OpenParenthesis.scriptToken, "No opening parenthesis found for function " + Name); string parameter = CelesteCompiler.PopToken(); CompiledStatement tempContainer = new CompiledStatement(); while (parameter != CloseParenthesis.scriptToken) { Debug.Assert(CelesteCompiler.CompileToken(parameter, tempContainer), "Failed to compile input parameter " + parameter); parameter = CelesteCompiler.PopToken(); } // Add null references first for all of the parameters we are missing for (int i = tempContainer.ChildCount; i < ParameterNames.Count; i++) { // This will push null onto the stack for every parameter we have not provided an input for Reference refToNull = new Reference(null); refToNull.Compile(thisCallsParams, "null", tokens, lines); } // Then add the actual parameters we have inputted for (int i = tempContainer.ChildCount - 1; i >= 0; i--) { // Add our parameters in reverse order, so they get added to the stack in reverse order tempContainer.MoveChildAtIndex(i, thisCallsParams); } // Add a reference to this function in our compile tree after we have added all of the inputs base.Compile(parent, token, tokens, lines); } else { // If we have no brackets, we are trying to compile the function as a reference rather than as a call (for use in equality for example) Reference funcRef = new Reference(this); funcRef.Compile(parent, Name, tokens, lines); } // Any local variable that is not set will be null // Any extra parameters will be added, but because we add them in reverse order, if they are not needed they will just be thrown away on the stack }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); // Unary operators act on other variables/values and so they must be included in the same token as another token (e.g. !var for example) // We remove the script token for the operator and split the other token out Debug.Assert(token.Length > 1); string rest = token.Remove(0, 1); // Removing wrong thing here // Add this operator to the tree parent.Add(this); // Parse the rest of the token which we will act on if (CelesteCompiler.CompileToken(rest, parent)) { // Take the value that has been added to the root and add it under this operator instead parent.MoveChildAtIndex(parent.ChildCount - 1, this); } else { // Error message if we cannot parse the next token Debug.Fail("Could not compile token: " + token + " in operator " + token); } }
/// <summary> /// If our first child is a binary operator, we swap it with this binary operator. /// This has the effect of evaluating this before the child operator. /// Useful when you wish to evaluate this operator before an assignment operator for example. /// </summary> protected void SwapWithChildBinaryOperator(CompiledStatement parent) { Debug.Assert(ChildCount > 0); Debug.Assert(ChildCompiledStatements[0] is AssignmentOperator); CompiledStatement child = ChildCompiledStatements[0]; // We start with: // // parent // / // this // / \ // child C // / \ // A B MoveChildAtIndex(0, parent); // Move the child to the parent - we now have: // // parent // / \ // this child // / / \ // C A B Debug.Assert(parent.ChildCompiledStatements[parent.ChildCount - 2] == this); parent.MoveChildAtIndex(parent.ChildCount - 2, child); // Move this to be underneath the child operator - we now have: // // parent // \ // child // / | \ // A B this // \ // C Debug.Assert(child.ChildCount == 3); child.MoveChildAtIndex(1, this); // Move the B to under this - we now have: // // parent // \ // child // / \ // A this // / \ // C B Debug.Assert(ChildCount == 2); MoveChildAtIndex(0, this); // Extract and then reinsert C into this - this swaps C and B to give: // // parent // \ // child // / \ // A this // / \ // B C }