/// <summary>Adds a method that was found into this classes set of methods to compile.</summary> /// <param name="fragment">The first fragment of the method, used for generating errors. This gives a valid line number.</param> /// <param name="body">The block of code for this method.</param> /// <param name="name">The name of the method. Null if anonymous is true.</param> /// <param name="anonymous">True if this method is an anonymous one and requires a name.</param> /// <param name="parameters">The set of parameters for this method.</param> /// <param name="returnType">The type that this method returns.</param> /// <param name="isPublic">True if this is a public method; false for private.</param> /// <returns>The first fragment following the method, if there is one.</returns> protected virtual CodeFragment AddFoundMethod(CodeFragment fragment, CodeFragment body, string name, bool anonymous, BracketFragment parameters, TypeFragment returnType, bool isPublic) { if (body == null) { fragment.Error("Invalid function definition (" + name + "). The content block {} is missing or isnt valid."); } if (anonymous) { name = "Compiler-Generated-$" + AnonymousCount; AnonymousCount++; } // The following is the explicit code block for this function: BracketFragment codeBlock = (BracketFragment)body; CompiledMethod cMethod = new CompiledMethod(this, name, parameters, codeBlock, returnType, isPublic); MethodOverloads set = MakeOrFind(name, cMethod.Builder.ReturnType); CodeFragment next = body.NextChild; if (anonymous) { CodeFragment newChild = DynamicMethodCompiler.Compile(cMethod, name, set.ReturnType, new ThisOperation(cMethod)); newChild.AddAfter(body); } body.Remove(); set.AddMethod(cMethod); FindMethods(codeBlock); return(next); }
/// <summary>Adds a method that was found into this classes set of methods to compile.</summary> /// <param name="fragment">The first fragment of the method, used for generating errors. This gives a valid line number.</param> /// <param name="body">The block of code for this method.</param> /// <param name="name">The name of the method. Null if anonymous is true.</param> /// <param name="anonymous">True if this method is an anonymous one and requires a name.</param> /// <param name="parameters">The set of parameters for this method.</param> /// <param name="returnType">The type that this method returns.</param> /// <param name="isPublic">True if this is a public method; false for private.</param> /// <returns>The first fragment following the method, if there is one.</returns> protected virtual CodeFragment AddFoundMethod(CodeFragment fragment,CodeFragment body,string name,bool anonymous,BracketFragment parameters,TypeFragment returnType,bool isPublic){ if(body==null){ fragment.Error("Invalid function definition ("+name+"). The content block {} is missing or isnt valid."); } if(anonymous){ name="Compiler-Generated-$"+AnonymousCount; AnonymousCount++; } // The following is the explicit code block for this function: BracketFragment codeBlock=(BracketFragment)body; CompiledMethod cMethod=new CompiledMethod(this,name,parameters,codeBlock,returnType,isPublic); MethodOverloads set=MakeOrFind(name,cMethod.Builder.ReturnType); CodeFragment next=body.NextChild; if(anonymous){ CodeFragment newChild=DynamicMethodCompiler.Compile(cMethod,name,set.ReturnType,new ThisOperation(cMethod)); newChild.AddAfter(body); } body.Remove(); set.AddMethod(cMethod); FindMethods(codeBlock); return next; }
/// <summary>Finds methods within the given fragment by looking for 'function'.</summary> /// <param name="fragment">The fragment to search.</param> public void FindMethods(CodeFragment fragment){ CodeFragment child=fragment.FirstChild; while(child!=null){ CodeFragment next=child.NextChild; if(child.IsParent){ FindMethods(child); } try{ if(child.GetType()==typeof(VariableFragment)){ VariableFragment vfrag=((VariableFragment)child); CodeFragment toRemove=null; string Value=vfrag.Value; if(Value=="function"){ // Found a function. bool isPublic; Modifiers.Handle(vfrag,out isPublic); // The return type could be on the function word (function:String{return "hey!";}) TypeFragment returnType=child.GivenType; toRemove=child; child=child.NextChild; if(child==null){ fragment.Error("Keyword 'function' can't be used on its own."); } toRemove.Remove(); string name=""; bool anonymous=false; BracketFragment parameters=null; if(child.GetType()==typeof(MethodFragment)){ MethodFragment method=(MethodFragment)child; name=((VariableFragment)(method.MethodName)).Value; parameters=method.Brackets; toRemove=child; child=child.NextChild; toRemove.Remove(); returnType=method.GivenType; }else if(child.GetType()==typeof(VariableFragment)){ // Found the name vfrag=(VariableFragment)child; if(vfrag.IsKeyword()){ vfrag.Error("Keywords cannot be used as function names ("+vfrag.Value+")."); } name=vfrag.Value; if(returnType==null){ returnType=child.GivenType; } toRemove=child; child=child.NextChild; toRemove.Remove(); if(child==null){ fragment.Error("Invalid function definition ("+name+"). All brackets are missing or arent valid."); } }else{ anonymous=true; } next=AddFoundMethod(fragment,child,name,anonymous,parameters,returnType,isPublic); } }else if(child.GetType()==typeof(MethodFragment)){ // Looking for anonymous methods ( defined as function() ) MethodFragment method=(MethodFragment)child; if(method.MethodName.GetType()==typeof(VariableFragment)){ VariableFragment methodName=((VariableFragment)(method.MethodName)); string name=methodName.Value; if(name=="function"){ // Found an anonymous function, function():RETURN_TYPE{}. // Note that function{}; is also possible and is handled above. CodeFragment toRemove=child; child=child.NextChild; toRemove.Remove(); next=AddFoundMethod(fragment,child,null,true,method.Brackets,method.GivenType,true); }else if(method.Brackets!=null){ FindMethods(method.Brackets); } }else if(method.Brackets!=null){ FindMethods(method.Brackets); } } }catch(CompilationException e){ if(e.LineNumber==-1 && child!=null){ // Setup line number: e.LineNumber=child.GetLineNumber(); } // Rethrow: throw e; } child=next; } }
/// <summary>Finds methods within the given fragment by looking for 'function'.</summary> /// <param name="fragment">The fragment to search.</param> public void FindMethods(CodeFragment fragment) { CodeFragment child = fragment.FirstChild; while (child != null) { CodeFragment next = child.NextChild; if (child.IsParent) { FindMethods(child); } try{ if (child.GetType() == typeof(VariableFragment)) { VariableFragment vfrag = ((VariableFragment)child); CodeFragment toRemove = null; string Value = vfrag.Value; if (Value == "function") { // Found a function. bool isPublic; Modifiers.Handle(vfrag, out isPublic); // The return type could be on the function word (function:String{return "hey!";}) TypeFragment returnType = child.GivenType; toRemove = child; child = child.NextChild; if (child == null) { fragment.Error("Keyword 'function' can't be used on its own."); } toRemove.Remove(); string name = ""; bool anonymous = false; BracketFragment parameters = null; if (child.GetType() == typeof(MethodFragment)) { MethodFragment method = (MethodFragment)child; name = ((VariableFragment)(method.MethodName)).Value; parameters = method.Brackets; toRemove = child; child = child.NextChild; toRemove.Remove(); returnType = method.GivenType; } else if (child.GetType() == typeof(VariableFragment)) { // Found the name vfrag = (VariableFragment)child; if (vfrag.IsKeyword()) { vfrag.Error("Keywords cannot be used as function names (" + vfrag.Value + ")."); } name = vfrag.Value; if (returnType == null) { returnType = child.GivenType; } toRemove = child; child = child.NextChild; toRemove.Remove(); if (child == null) { fragment.Error("Invalid function definition (" + name + "). All brackets are missing or arent valid."); } } else { anonymous = true; } next = AddFoundMethod(fragment, child, name, anonymous, parameters, returnType, isPublic); } } else if (child.GetType() == typeof(MethodFragment)) { // Looking for anonymous methods ( defined as function() ) MethodFragment method = (MethodFragment)child; if (method.MethodName.GetType() == typeof(VariableFragment)) { VariableFragment methodName = ((VariableFragment)(method.MethodName)); string name = methodName.Value; if (name == "function") { // Found an anonymous function, function():RETURN_TYPE{}. // Note that function{}; is also possible and is handled above. CodeFragment toRemove = child; child = child.NextChild; toRemove.Remove(); next = AddFoundMethod(fragment, child, null, true, method.Brackets, method.GivenType, true); } else if (method.Brackets != null) { FindMethods(method.Brackets); } } else if (method.Brackets != null) { FindMethods(method.Brackets); } } }catch (CompilationException e) { if (e.LineNumber == -1 && child != null) { // Setup line number: e.LineNumber = child.GetLineNumber(); } // Rethrow: throw e; } child = next; } }
/// <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 all operations in a given fragment into executable IL.</summary> /// <param name="fragment">The parent fragment. Most likely represents a pair of brackets.</param> /// <param name="block">The method that the operations represent.</param> /// <returns>True if the block returns something.</returns> public static bool CompileOperations(CodeFragment fragment, CompiledMethod block) { CodeFragment child = fragment.FirstChild; bool returns = false; while (child != null) { if (Types.IsTypeOf(child, typeof(Operation))) { Operation operation = (Operation)child; Type t = operation.GetType(); try{ if (operation.RequiresStoring) { child.Error("Unexpected operation or value - you must give somewhere to hold the result of this."); } // Add the above operation to the block's IL: operation.OutputIL(block.ILStream); }catch (CompilationException e) { // Setup line number: if (e.LineNumber == -1) { e.LineNumber = operation.LineNumber; } // Rethrow: throw e; } if (t == typeof(MethodOperation)) { Type returnType = (((MethodOperation)operation).MethodToCall).ReturnType; if (returnType != null && returnType != typeof(void)) { // Note this doesn't include (nitro) Void because it's an object as far as .net is concerned! block.ILStream.Emit(OpCodes.Pop); } } returns = (t == typeof(ReturnOperation) || (t == typeof(IfOperation) && ((IfOperation)operation).AllRoutesReturn)); if (returns) { if (child.NextChild != null) { Wrench.Log.Add("Warning: Unreachable code detected at line " + child.NextChild.GetLineNumber()); child.NextChild = null; } break; } } child = child.NextChild; } return(returns); }