/// <summary> /// Compiles an individual token /// </summary> /// <param name="token"></param> public static bool CompileToken(string token, CompiledStatement parent) { if (CompileAsValue(parent, token)) { return(true); } else if (CompileAsVariable(parent, token)) { return(true); } else if (CompileAsFlowControl(parent, token)) { return(true); } else if (CompileAsKeyword(parent, token)) { return(true); } else if (CompileAsOperator(parent, token)) { return(true); } else { Debug.Fail("Unrecognised token " + token); return(false); } }
/// <summary> /// Create an instance of the inputted compiled statement /// </summary> /// <typeparam name="T"></typeparam> /// <param name="parent"></param> /// <param name="type"></param> private static void Create <T>(CompiledStatement parent, string token, Type type) where T : CompiledStatement { // Create an instance of our keyword T statement = (T)Activator.CreateInstance(type); statement.Compile(parent, token, Tokens, Lines); }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); Delimiter.InlineToken = scriptToken; // Now check that there is another element after our keyword that we can use as the variable name Debug.Assert(tokens.Count > 0, "No value found for the right hand side of keyword: " + token); if (tokens.First.Value == FunctionKeyword.scriptToken) { // If we have a function after this, we do nothing - functions by default are added to the current local scope Delimiter.InlineToken = ""; return; } // Get the next token that appears on the right hand side of this operator - this will be our variable name string rhsOfKeyword = CelesteCompiler.PopToken(); Debug.Assert(!CelesteStack.CurrentScope.VariableExists(rhsOfKeyword), "Variable with the same name already exists in this scope"); // Creates a new variable, but does not call Compile - Compile for variable assigns a reference from the stored variable in CelesteStack Variable variable = CelesteStack.CurrentScope.CreateLocalVariable <Variable>(rhsOfKeyword); // If we still have unparsed tokens, then add this variable to the tree // Otherwise, do not - we do not need to push an object onto the stack for no reason if (tokens.Count > 0) { // Add our variable underneath our parent - this keyword will do nothing at run time so do not add it to the tree parent.Add(variable); } }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); // Find the first occurrence of the delimiter and split the string at that index int delimiterIndex = token.IndexOf(scriptTokenChar); Debug.Assert(delimiterIndex > 0); // Must exist and cannot be the first element in a string // Split the token into two strings - the lhs of the first delimiter and the remaining rhs after the delimiter string lhsOfDelimiter = token.Substring(0, delimiterIndex); string rhsOfDelimiter = token.Substring(delimiterIndex + 1); // We add these in the reverse order so that they appear as LHS, INLINE, RHS (the tokens list is basically a stack) if (!string.IsNullOrEmpty(rhsOfDelimiter)) { // Add anything leftover on the rhs tokens.AddFirst(rhsOfDelimiter); } if (!string.IsNullOrEmpty(InlineToken)) { // Add our inlined token if it is not empty // If it is empty, then it was merely acting as a separator tokens.AddFirst(InlineToken); } // Add the lhs of delimiter - this cannot be empty Debug.Assert(!string.IsNullOrEmpty(lhsOfDelimiter)); tokens.AddFirst(lhsOfDelimiter); }
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); } }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); // We know the token will at least start with the start delimiter if (token.Length > 1) { // In here if the start delimiter and another token are smushed together with no space // Split the start delimiter and the rest of the token and add the rest of the token to the start of our tokens list string rest = token.Remove(0, 1); tokens.AddFirst(rest); } // We keep parsing until we find a closing character for our list bool foundClosing = false; while (!foundClosing) { Debug.Assert(tokens.Count > 0 || lines.Count > 0, "List requires a closing " + endDelimiter); // Create a new set of tokens if we have run out for this line if (tokens.Count == 0) { CelesteCompiler.TokenizeNextLine(); } string nextToken = CelesteCompiler.PopToken(); if (nextToken.EndsWith(endDelimiter) && nextToken.Length > 1) { // Our token has the end delimiter squashed at the end of it, so we split the token in two and add the delimiter to the tokens list // This will mean we still compile this token and finish our list the next iteration round nextToken = nextToken.Remove(nextToken.Length - 1); tokens.AddFirst(endDelimiter); } if (nextToken == endDelimiter) { foundClosing = true; } else if (CelesteCompiler.CompileToken(nextToken, parent)) { // Take the value that has been created from compiling this token and add it to our list // Then remove the compiled statement - we do not want objects in our list pushed onto the stack CompiledStatement valueCreated = parent.ChildCompiledStatements[parent.ChildCount - 1]; parent.RemoveAt(parent.ChildCount - 1); // The value we created MUST be a value Debug.Assert(valueCreated is Value); ListRef.Add((valueCreated as Value)._Value); } else { // Error message if we cannot parse the next token Debug.Fail("Error parsing token: " + token + " in list"); } } }
/// <summary> /// Removes a child from this compile statement's children and returns it for adding somewhere else /// </summary> /// <param name="index"></param> /// <returns></returns> public CompiledStatement RemoveAt(int index) { Debug.Assert(ChildCompiledStatements.Count > index); CompiledStatement child = ChildCompiledStatements[index]; ChildCompiledStatements.RemoveAt(index); return(child); }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); bool result; bool.TryParse(token, out result); _Value = result; }
/// <summary> /// Division should take precedence over Assignment /// </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) { base.Compile(parent, token, tokens, lines); if (ChildCompiledStatements[0] is AssignmentOperator) { SwapWithChildBinaryOperator(parent); } }
/// <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); // If we have a child assignment operator (it can only be in the first position, we swap it with this operator - this should act first // This means we have an expression of the form: A = B - C if (ChildCompiledStatements[0] is AssignmentOperator) { SwapWithChildBinaryOperator(parent); } }
/// <summary> /// See if we have referenced a local variable that exists and if so, create a reference object to it in our expression tree. /// This will push a reference onto the stack /// </summary> /// <param name="parent"></param> /// <param name="token"></param> /// <returns></returns> private static bool CompileAsVariable(CompiledStatement parent, string token) { if (!CelesteStack.CurrentScope.VariableExists(token, ScopeSearchOption.kUpwardsRecursive)) { return(false); } // Call compile on our variable we created - this will push a reference to it onto the stack CelesteStack.CurrentScope.GetLocalVariable(token).Compile(parent, token, Tokens, Lines); return(true); }
/// <summary> /// Runs through each keyword to see if we have a valid keyword our token fits /// </summary> /// <param name="parent"></param> /// <param name="token"></param> /// <returns></returns> private static bool CompileAsKeyword(CompiledStatement parent, string token) { if (!RegisteredKeywords.ContainsKey(token)) { return(false); } // Create an instance of our keyword Create <Keyword>(parent, token, RegisteredKeywords[token]); return(true); }
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 object on the left hand side of the 'null' keyword"); // Create a value object with null as the stored value - it will be moved under the equality operator when we call nullValue.Compile // This will push null onto the stack when run Value nullValue = new Value(null); nullValue.Compile(parent, token, tokens, lines); }
/// <summary> /// Attempt to parse the inputted token as an operator. /// Will rearrange the values to act on underneath the operator in our compiled tree. /// </summary> /// <param name="token"></param> /// <param name="statements"></param> /// <returns></returns> private static bool CompileAsOperator(CompiledStatement parent, string token) { foreach (KeyValuePair <MethodInfo, Type> pair in RegisteredOperators) { if ((bool)pair.Key.Invoke(null, new object[] { token })) { Create <Operator>(parent, token, pair.Value); return(true); } } return(false); }
/// <summary> /// Runs through each type to see if we have a valid value our token fits /// </summary> /// <param name="token"></param> /// <returns></returns> private static bool CompileAsValue(CompiledStatement parent, string token) { foreach (KeyValuePair <MethodInfo, Type> pair in RegisteredValues) { // Invoke the method we have picked up from reflection for seeing if this token is a valid value if ((bool)pair.Key.Invoke(null, new object[] { token })) { Create <Value>(parent, token, pair.Value); return(true); } } return(false); }
/// <summary> /// Adds a not unary operator with the next token's compiled object to the parent /// </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) { base.Compile(parent, token, tokens, lines); // Now check that there is an element after our keyword that we can use as the variable name Debug.Assert(tokens.Count > 0, "No value found for the right hand side of the 'not' keyword"); string rhsToken = CelesteCompiler.PopToken(); // Create a not operator and compile - this will do all the work of moving statements around in the parent // It will also add itself to the parent tree NotOperator notOperator = new NotOperator(); notOperator.Compile(parent, NotOperator.scriptToken + rhsToken, tokens, lines); }
/// <summary> /// Adds an and binary operator with the previous compiled statement and next token's compiled object to the parent /// </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) { base.Compile(parent, token, tokens, lines); Debug.Assert(parent.ChildCount > 0, "Must have an object on the left side of the 'and' keyword"); // Now check that there is another element after our keyword that we can use as the variable name Debug.Assert(tokens.Count > 0, "No value found for the right hand side of the 'and' keyword"); // Create an and operator and compile - this will do all the work of moving statements around in the parent // It will also add itself to the parent tree AndOperator andOperator = new AndOperator(); andOperator.Compile(parent, AndOperator.scriptToken, tokens, lines); }
public override void PerformOperation() { // Set up our parameters if required if (ParameterNames.Count > 0) { Debug.Assert(ChildCount > 0, "Fatal error in function call - no arguments to process"); CompiledStatement thisCallsParams = ChildCompiledStatements[0]; ChildCompiledStatements.RemoveAt(0); // This will run through each stored parameter and add them to the stack thisCallsParams.PerformOperation(); } // Set up our parameters - read them off the stack with the first parameter at the top for (int i = 0; i < ParameterNames.Count; i++) { Debug.Assert(CelesteStack.StackSize > 0, "Insufficient parameters to function"); CelesteObject input = CelesteStack.Pop(); Variable functionParameter = FunctionScope.GetLocalVariable(ParameterNames[i], ScopeSearchOption.kThisScope); if (input.IsReference()) { functionParameter.Value = input.ValueImpl; } else { (functionParameter.Value as Reference).Value = input.Value; } } CelesteStack.CurrentScope = FunctionScope; // Performs all the operators on the children first foreach (CompiledStatement statement in FuncImpl.ChildCompiledStatements) { statement.PerformOperation(); if (statement is ReturnKeyword) { // Stop iterating through if we have hit a ReturnKeyword in our function break; } } CelesteStack.Scopes.Remove(FunctionScope); CelesteStack.CurrentScope = FunctionScope.ParentScope; }
/// <summary> /// Reads in the condition for the while loop and then the body to perform while the condition is true. /// </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) { base.Compile(parent, token, tokens, lines); KeyValuePair <CompiledStatement, CompiledStatement> whileCondBody = new KeyValuePair <CompiledStatement, CompiledStatement>(new CompiledStatement(), new CompiledStatement()); ConditionsAndBodies.Add(whileCondBody); Debug.Assert(tokens.Count > 0, "Tokens required for while condition"); while (tokens.Count > 0) { CelesteCompiler.CompileToken(CelesteCompiler.PopToken(), whileCondBody.Key); } // We keep parsing until we find the closing keyword for our while control bool foundClosing = false; while (!foundClosing) { Debug.Assert(tokens.Count > 0 || lines.Count > 0, "Function requires a closing " + endDelimiter); // Create a new set of tokens if we have run out for this line if (tokens.Count == 0) { CelesteCompiler.TokenizeNextLine(); } string nextToken = CelesteCompiler.PopToken(); if (nextToken == endDelimiter) { foundClosing = true; } else if (CelesteCompiler.CompileToken(nextToken, whileCondBody.Value)) { } else { // Error message if we cannot parse the next token Debug.Fail("Operator invalid on token: " + token); } } // Close the scope that is automatically opened in our constructor CelesteStack.CurrentScope = FlowScope.ParentScope; }
/// <summary> /// Compiles the parsed file down into a statement tree /// </summary> /// <param name="parsedFile"></param> private static Tuple <bool, CompiledStatement> CompileScript(List <string> parsedFile) { RootStatement = new CompiledStatement(); Tokens = new LinkedList <string>(); Lines = new LinkedList <string>(parsedFile); while (Lines.Count > 0) { // As soon as we detect an error, we stop parsing the script and return a tuple with a false flag and a null compile tree if (!CompileLine()) { Debug.Fail("Error compiling script"); return(new Tuple <bool, CompiledStatement>(false, null)); } } return(new Tuple <bool, CompiledStatement>(true, RootStatement)); }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); // Now check that there is another element after our keyword that we can use as the variable name Debug.Assert(tokens.Count > 0, "No value found for the right hand side of keyword: " + token); // Get the next token that appears on the right hand side of this operator - this will be our variable name string rhsOfKeyword = CelesteCompiler.PopToken(); if (rhsOfKeyword == FunctionKeyword.scriptToken) { Debug.Assert(tokens.Count > 0, "Function name must exist"); string functionName = tokens.First.Value; functionName = functionName.Remove(functionName.IndexOf(OpenParenthesis.scriptToken)); if (CelesteCompiler.CompileToken(rhsOfKeyword, parent)) { // If we have compiled our global function, we need to remove it from the current scope it is added to by default and move it to the global scope Debug.Assert(CelesteStack.CurrentScope.VariableExists(functionName)); Variable function = CelesteStack.CurrentScope.RemoveLocalVariable(functionName); CelesteStack.GlobalScope.AddLocalVariable(function); } else { Debug.Fail("Error parsing global function"); } return; } Debug.Assert(!CelesteStack.GlobalScope.VariableExists(rhsOfKeyword), "Variable with the same name already exists in this scope"); // Creates a new variable, but does not call Compile - Compile for variable assigns a reference from the stored variable in CelesteStack Variable variable = CelesteStack.GlobalScope.CreateLocalVariable <Variable>(rhsOfKeyword); // If we still have unparsed tokens, then add this variable to the tree // Otherwise, do not - we do not need to push an object onto the stack for no reason if (tokens.Count > 0) { // Add our variable underneath our parent - this keyword will do nothing at run time so do not add it to the tree parent.Add(variable); } }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); parent.Add(this); while (tokens.Count > 0) { // Get the next token that appears on the right hand side of this operator // This will be the name of the local variables we wish to return string rhsOfKeyword = CelesteCompiler.PopToken(); if (rhsOfKeyword.EndsWith(returnParameterDelimiter)) { // Return the parameter delimiter after each return parameter if it exists rhsOfKeyword = rhsOfKeyword.Remove(rhsOfKeyword.Length - 1); } Debug.Assert(CelesteCompiler.CompileToken(rhsOfKeyword, this), "Error compiling return parameter"); } }
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> /// Extracts the script token from the inputted token and pushes the remaining token back into the tokens list followed by the script token. /// </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) { base.Compile(parent, token, tokens, lines); // For now we will just take the parenthesis and add it as a separate token along with the rest of the token // Need to do actual implementation stuff though int index = token.IndexOf(scriptToken); string lhsString = token.Substring(0, index); string rhsString = token.Substring(index + 1); // Split the string with the parenthesis but maintain the order of the tokens in the list from left to right, first to last if (!string.IsNullOrEmpty(rhsString)) { tokens.AddFirst(rhsString); } tokens.AddFirst(scriptToken); if (!string.IsNullOrEmpty(lhsString)) { tokens.AddFirst(lhsString); } }
/// <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 }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); string fullString = ""; // If our token is of the form "something" we have our full string if (token.EndsWith(endDelimiter)) { // Remove the '"' from the start and the end fullString = token.Substring(1, token.Length - 2); } else { // Our full string is going to be split over many tokens - we need to keep adding tokens until we hit our second '"' // Remove the '"' from the first token fullString = token.Remove(0, 1); // Need more tokens left over to make our full string Debug.Assert(tokens.Count > 0, "No \" found to end the string: " + fullString); string currentToken; // We are going to keep removing elements, so need to check whether we still have tokens left to check while (tokens.Count > 0) { currentToken = CelesteCompiler.PopToken(); if (!string.IsNullOrEmpty(currentToken)) { // If the next token we are moving through contains '"' we are done finding our full string if (currentToken.Contains(endDelimiter)) { // Add the substring without the end string character to our full string int index = currentToken.IndexOf(endDelimiter); if (index > 0) { // Concatenate the contents of this token if it was more than just the end delimiter fullString += " " + currentToken.Substring(0, index); } if (index < currentToken.Length - 1) { // The end delimiter was in the middle of the token and we have other stuff that should be popped back into the tokens list // index + 1 because we don't want to add the end delimiter tokens.AddFirst(currentToken.Substring(index + 1)); } break; } else { fullString += " " + currentToken; } } // We should only ever get here if we have tokens left // If this assert triggers, it means that we have run out of tokens, but not found a close to our string Debug.Assert(tokens.Count > 0, "No \" found to end the string: " + fullString); } } _Value = fullString; }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); parent.Add(this); }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); // We know the token will at least start with the start delimiter if (token.Length > 1) { // In here if the start delimiter and another token are smushed together with no space // Split the start delimiter and the rest of the token and add the rest of the token to the start of our tokens list string rest = token.Remove(0, 1); tokens.AddFirst(rest); } // We keep parsing until we find a closing character for our list bool foundClosing = false; while (!foundClosing) { Debug.Assert(tokens.Count > 0 || lines.Count > 0, "Table requires a closing " + endDelimiter); // Create a new set of tokens if we have run out for this line if (tokens.Count == 0) { CelesteCompiler.TokenizeNextLine(); } string nextToken = CelesteCompiler.PopToken(); if (nextToken == AssignmentOperator.scriptToken) { string valueToken = tokens.First.Value; if (valueToken.EndsWith(endDelimiter) && valueToken.Length > 1) { valueToken = valueToken.Remove(valueToken.Length - 1, 1); tokens.First.Value = valueToken; foundClosing = true; } } if (nextToken == endDelimiter) { foundClosing = true; } else if (CelesteCompiler.CompileToken(nextToken, parent)) { CompiledStatement compiledStatement = parent.ChildCompiledStatements[parent.ChildCount - 1]; if (!gotKey) { // The statement we created for our key MUST be a value type Debug.Assert(compiledStatement is Value, "Key in table must be a valid value type"); gotKey = true; } else if (!gotEquality) { // The statement we created after our key MUST be the equality operator Debug.Assert(compiledStatement is AssignmentOperator, "Equality expected after key"); gotEquality = true; gotValue = compiledStatement.ChildCount == 2; } if (gotValue) { // We have an equals parented under the 'parent' parameter (along with this table), with the key and value parented under that // We insert the key and value into our dictionary, discard the equals statement and adjust our flags Debug.Assert(parent.ChildCount >= 2); Debug.Assert(parent.ChildCompiledStatements[parent.ChildCount - 1].ChildCount == 2); Debug.Assert(parent.ChildCompiledStatements[parent.ChildCount - 1] is AssignmentOperator); AssignmentOperator equals = parent.ChildCompiledStatements[parent.ChildCount - 1] as AssignmentOperator; Debug.Assert(equals.ChildCompiledStatements[0] is Value); Debug.Assert(equals.ChildCompiledStatements[1] is Value); DictionaryRef.Add((equals.ChildCompiledStatements[0] as Value)._Value, (equals.ChildCompiledStatements[1] as Value)._Value); parent.ChildCompiledStatements.RemoveAt(parent.ChildCount - 1); gotKey = false; gotEquality = false; gotValue = false; } } else { // Error message if we cannot parse the next token Debug.Fail("Operator invalid on token: " + token); } } Debug.Assert(!gotKey && !gotEquality && !gotValue, "Incomplete key value pair in table"); }
public override void Compile(CompiledStatement parent, string token, LinkedList <string> tokens, LinkedList <string> lines) { base.Compile(parent, token, tokens, lines); // Now check that there is another element after our keyword that we can use as the function name Debug.Assert(tokens.Count > 0 || lines.Count > 0, "No parameters or body found for the keyword: " + token); // The first token should be the function name string functionName = CelesteCompiler.PopToken(); Debug.Assert(!CelesteStack.CurrentScope.VariableExists(functionName), "Variable with the same name already exists in this scope"); // Get the next token - this should be an opening parenthesis string openingParenthesis = CelesteCompiler.PopToken(); Debug.Assert(openingParenthesis == OpenParenthesis.scriptToken, "No opening parenthesis found for function " + functionName); // Creates a new function, but does not call Compile - Compile for function assigns a reference from the stored function in CelesteStack Function function = CelesteStack.CurrentScope.CreateLocalVariable <Function>(functionName); // Obtain the parameter names from the strings/tokens between the brackets List <string> paramNames = new List <string>(); string parameters = CelesteCompiler.PopToken(); while (parameters != CloseParenthesis.scriptToken) { // Clean the parameter array of empty strings and remove any delimiter characters from parameter names List <string> parameterList = new List <string>(parameters.Split(Delimiter.scriptTokenChar)); parameterList.RemoveAll(x => string.IsNullOrEmpty(x)); parameterList = new List <string>(parameterList.Select(x => x = x.Replace(Delimiter.scriptToken, ""))); // Add any parameters which are of this form 'param1,param2' paramNames.AddRange(parameterList); parameters = CelesteCompiler.PopToken();// This algorithm should completely take care of any mix of parameters separated with a space or not } function.SetParameters(paramNames.ToArray()); // We keep parsing until we find the closing keyword for our function bool foundClosing = false; while (!foundClosing) { Debug.Assert(tokens.Count > 0 || lines.Count > 0, "Function requires a closing " + endDelimiter); // Create a new set of tokens if we have run out for this line if (tokens.Count == 0) { CelesteCompiler.TokenizeNextLine(); } string nextToken = CelesteCompiler.PopToken(); if (nextToken == endDelimiter) { foundClosing = true; } else if (CelesteCompiler.CompileToken(nextToken, function.FuncImpl)) { } else { // Error message if we cannot parse the next token Debug.Fail("Operator invalid on token: " + token); } } // Do not add the function - it will be looked up to and called rather than pushed onto the stack (much like a variable) // Close the function's scope now - we have added all the appropriate variables to it // This scope is automatically opened in the Function constructor CelesteStack.CurrentScope = function.FunctionScope.ParentScope; }