/// <summary>Finds which type of fragment will accept and handle the given character.</summary> /// <param name="peek">The character to find a handler for.</param> /// <returns>The handler which will deal with this character. May also be told to stop if no handler is available.</returns> public static Handler Find(char peek) { if (peek == StringReader.NULL || BracketFragment.IsEndBracket(peek) != -1) { return(Handler.Stop); } else if (BracketFragment.WillHandle(peek)) { return(Handler.Brackets); } else if (StringFragment.WillHandle(peek)) { return(Handler.String); } else if (TypeFragment.WillHandle(peek)) { return(Handler.Type); } else if (OperatorFragment.WillHandle(peek)) { return(Handler.Operator); } else if (PropertyFragment.WillHandle(peek)) { return(Handler.Property); } else if (NumberFragment.WillHandle(peek)) { return(Handler.Number); } return(Handler.Variable); }
/// <summary>Loads the parameter block into a set of types.</summary> private void ParseParameters() { if (DefaultParameterValues != null) { DefaultParameterValues = null; Builder.SetParameters(null); } if (ParameterBlock == null || !ParameterBlock.IsParent) { // No inputs anyway, e.g. test(){..} ParametersOk(); return; } // Each of blocks children is an operation segment. ParameterTypes = new Type[ParameterBlock.ChildCount()]; DefaultParameterValues = new CompiledFragment[ParameterTypes.Length]; int index = 0; // For each parameter.. CodeFragment current = ParameterBlock.FirstChild; while (current != null) { if (!current.IsParent) { Error("Invalid function definition input variable found."); } // Default value of this variable (if any). E.g. var1=true,var2.. CompiledFragment DefaultValue = null; CodeFragment inputName = current.FirstChild; if (inputName.GetType() != typeof(VariableFragment)) { Error("Invalid function definition inputs for " + Name + ". Must be (var1:type,var2:type[=a default value],var3:type..). [=..] is optional and can be used for any of the variables."); } string paramName = ((VariableFragment)inputName).Value; if (inputName.NextChild == null) { } else if (inputName.NextChild != null && !Types.IsTypeOf(inputName.NextChild, typeof(OperatorFragment))) { // No type OR default, or the block straight after isn't : or = Error("Invalid function parameters for " + Name + ". '" + paramName + "' is missing a type or default value."); } else { OperatorFragment opFrag = (OperatorFragment)inputName.NextChild; //it must be a set otherwise it's invalid. if (opFrag.Value == null || opFrag.Value.GetType() != typeof(OperatorSet)) { Error("Invalid default function parameter value provided for " + Name + ". '" + paramName + "'."); } current.FirstChild = inputName.NextChild.NextChild; if (!current.IsParent) { Error("Invalid default function definition. Must be (name:type=expr,..)"); } DefaultValue = current.Compile(this); } if (inputName.GivenType != null) { ParameterTypes[index] = inputName.GivenType.FindType(Script); } else if (DefaultValue != null) { ParameterTypes[index] = DefaultValue.OutputType(out DefaultValue); } else { Error("Parameter " + paramName + "'s type isn't given. Should be e.g. (" + paramName + ":String,..)."); } DefaultParameterValues[index] = DefaultValue; if (ParameterSet.ContainsKey(paramName)) { Error("Cant use the same parameter name twice. " + paramName + " in function " + Name + "."); } else if (ParameterTypes[index] == null) { Error("Type not given or invalid for parameter " + paramName + " in function " + Name + "."); } else { ParameterSet.Add(paramName, new ParameterVariable(paramName, ParameterTypes[index])); } current = current.NextChild; index++; } ApplyParameters(); }
/// <summary>Finds properties in the given fragment.</summary> /// <param name="fragment">The fragment to search.</param> public void FindProperties(CodeFragment fragment) { CodeFragment child = fragment.FirstChild; CodeFragment next = null; while (child != null) { next = child.NextChild; if (child.IsParent) { if (child.GetType() == typeof(OperationFragment)) { try{ // Is it private? Note that if it is, "private" is removed. bool isPublic = !Modifiers.Check(child.FirstChild, "private"); // Grab the property name: CodeFragment propName = child.FirstChild; if (propName == null) { child.Error("This value must be followed by something."); } if (propName.GetType() != typeof(VariableFragment)) { child.Error("Didn't recognise this as a property. Please note that all code you'd like to run immediately should be inside a function called Start, or in the constructor of a class."); } VariableFragment vfrag = ((VariableFragment)child.FirstChild); // These are never local: vfrag.AfterVar = false; string Name = vfrag.Value; if (vfrag.IsKeyword()) { child.Error("Can't use " + Name + " as a property because it's a keyword."); } if (Fields.ContainsKey(Name)) { child.Error(Name + " has been defined twice."); } CodeFragment defaultValue = null; if (vfrag.NextChild == null) { } else if (vfrag.NextChild != null && !Types.IsTypeOf(vfrag.NextChild, typeof(OperatorFragment))) { // No type OR default, or the block straight after isn't : or = child.Error(Name + " is missing a type or default value."); } else { OperatorFragment opFrag = (OperatorFragment)vfrag.NextChild; // It must be a set otherwise it's invalid. if (opFrag.Value == null || opFrag.Value.GetType() != typeof(OperatorSet)) { child.Error("Invalid default value provided for '" + Name + "'."); } defaultValue = opFrag.NextChild; } DefineField(Name, vfrag, isPublic, defaultValue); child.Remove(); if (defaultValue != null) { GetInit().CodeBlock.AddChild(child); } }catch (CompilationException e) { if (e.LineNumber == -1 && child != null) { // Setup line number: e.LineNumber = child.GetLineNumber(); } // Rethrow: throw e; } } else { FindProperties(child); } } child = next; } }
/// <summary>Compiles an operation fragment into an executable operation. The fragment may contain multiple /// operators and some may even be the same so the furthest right takes priority; 1+2+3 becomes 1+(2+3). /// The compiled fragments are placed back into the same operation fragment. When it's complete, the operation /// will only contain one compiled fragment.</summary> /// <param name="fragment">The operation fragment to compile.</param> /// <param name="parentBlock">The method the operations are compiling into.</param> public static void CompileOperators(OperationFragment fragment, CompiledMethod parentBlock) { CodeFragment child = fragment.FirstChild; OperatorFragment highestPriority = null; while (child != null) { if (child.GetType() == typeof(OperatorFragment)) { OperatorFragment thisOperator = (OperatorFragment)child; // <= below enforces furthest right: if (highestPriority == null || highestPriority.Value.Priority <= thisOperator.Value.Priority) { highestPriority = thisOperator; } } child = child.NextChild; } if (highestPriority == null) { return; } CodeFragment left = highestPriority.PreviousChild; CodeFragment right = highestPriority.NextChild; CodeFragment leftUsed = left; CodeFragment rightUsed = right; if (left == null || left.GetType() == typeof(OperatorFragment)) { leftUsed = null; } else if (!Types.IsCompiled(left)) { leftUsed = left.Compile(parentBlock); } if (right == null || right.GetType() == typeof(OperatorFragment)) { rightUsed = null; } else if (!Types.IsCompiled(right)) { rightUsed = right.Compile(parentBlock); } Operation newFragment = highestPriority.Value.ToOperation((CompiledFragment)leftUsed, (CompiledFragment)rightUsed, parentBlock); if (newFragment == null) { highestPriority.Error("Error: An operator has been used but with nothing to use it on! (It was a '" + highestPriority.Value.Pattern + "')"); } // Replace out Left, Right and the operator itself with the new fragment: highestPriority.Remove(); if (left == null) { newFragment.AddBefore(right); } else { newFragment.AddAfter(left); } if (rightUsed != null) { right.Remove(); } if (leftUsed != null) { left.Remove(); } // And call again to collect the rest: CompileOperators(fragment, parentBlock); }