public override void OutputIL(NitroIL into) { Type type2 = Input1.OutputType(out Input1); // Is it something which is being ["indexed"]? May apply to properties too. bool indexOperation = (Input0.GetType() == typeof(IndexOperation)); bool propertyOperation = (Input0.GetType() == typeof(PropertyOperation)); if (indexOperation || propertyOperation) { // Hook up what we will be setting for the index to handle if it needs to. // Note that the object will not change as we have already run it's OutputType call above. ((Operation)Input0).Input0 = Input1; } // Update input0 by computing the type it outputs: Type type1 = Input0.OutputType(out Input0); object value = Input0.ActiveValue(); if (value.GetType() != typeof(LocalVariable)) { // Local vars can change type so this doesn't affect them. if (type1 == null) { Error("Can't set to nothing."); } if (type2 == null) { if (type1.IsValueType) { Error("Can't set " + type1 + " to null because it's a value type."); } } else if (!type1.IsAssignableFrom(type2)) { Error("Can't implicity convert " + type2 + " to " + type1 + "."); } } if (Types.IsTypeOf(value, typeof(ISettable))) { ISettable Value = (ISettable)value; Value.OutputTarget(into); Input1.OutputIL(into); Value.OutputSet(into, type2); if (Output) { // Daisy chaining these sets. Input0.OutputIL(into); } } else if (indexOperation && value.GetType() == typeof(MethodOperation)) { // This is ok! We've called something like obj["hello"]=input1; // Just output the method call: Input0.OutputIL(into); if (Output) { Error("Can't daisy chain with an indexer. Place your indexed object furthest left."); } } else if (propertyOperation && value.GetType() == typeof(MethodOperation) && ((MethodOperation)value).MethodName == "set_Item") { // This is also ok - we've done something like object.property=value; and it mapped to object["property"]=value; // Just output the method call: Input0.OutputIL(into); if (Output) { Error("Can't daisy chain with a property set here. Place your indexed object furthest left."); } } else { Error("Attempted to set to something (a " + value.GetType() + ") that isn't a variable."); } }
/// <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>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(); }
public CompiledMethod(CompiledClass parent, string name, BracketFragment parameterBlock, BracketFragment codeBlock, TypeFragment retType, bool isPublic) { Name = name; Parent = parent; CodeBlock = codeBlock; Script = Parent.Script; ParameterBlock = parameterBlock; Type returnType = null; if (retType != null) { returnType = retType.FindType(Script); if (returnType == null) { Error("Type '" + retType.Value + "' was not found."); } } string methodName = Name; MethodAttributes attrib = isPublic?MethodAttributes.Public:MethodAttributes.Private; if (methodName == "new") { methodName = ".ctor"; attrib |= MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; } // Does the parent base type define this method? // If so, use it's name. Type baseType = Parent.Builder.BaseType; // Parse the parameter set right away: ParseParameters(); MethodInfo mInfo = Types.GetOverload(baseType.GetMethods(), Name, ParameterTypes, true); if (mInfo != null) { methodName = mInfo.Name; attrib |= MethodAttributes.Virtual | MethodAttributes.HideBySig; //|MethodAttributes.NewSlot; } bool isVoid = Types.IsVoid(returnType); if (isVoid) { returnType = typeof(void); } Builder = Parent.Builder.DefineMethod( methodName, attrib, returnType, null ); ApplyParameters(); ILStream = new NitroIL(Builder.GetILGenerator()); EndOfMethod = ILStream.DefineLabel(); if (!isVoid) { ReturnBay = ILStream.DeclareLocal(returnType); } }
/// <summary>Emits the Value held by this compiled fragment if possible.</summary> /// <param name="type">The type of the value (or one of the base types of the value).</param> /// <param name="into">The IL stream to output the IL into.</param> public void EmitValue(Type type, NitroIL into) { if (type == typeof(string)) { into.Emit(OpCodes.Ldstr, (string)Value); } else if (type == typeof(int)) { into.Emit(OpCodes.Ldc_I4, (int)Value); } else if (type == typeof(uint)) { into.Emit(OpCodes.Ldc_I4, (uint)Value); } else if (type == typeof(long)) { into.Emit(OpCodes.Ldc_I8, (long)Value); } else if (type == typeof(ulong)) { into.Emit(OpCodes.Ldc_I8, (ulong)Value); } else if (type == typeof(float)) { into.Emit(OpCodes.Ldc_R4, (float)Value); } else if (type == typeof(double)) { into.Emit(OpCodes.Ldc_R8, (double)Value); } else if (type == typeof(OpCode)) { into.Emit((OpCode)Value); } else if (type == typeof(bool)) { into.Emit(OpCodes.Ldc_I4, ((bool)Value)?1:0); } else if (type == typeof(short)) { into.Emit(OpCodes.Ldc_I4, (short)Value); } else if (type == typeof(ushort)) { into.Emit(OpCodes.Ldc_I4, (ushort)Value); } else if (type == typeof(byte)) { into.Emit(OpCodes.Ldc_I4, (byte)Value); } else if (type == typeof(sbyte)) { into.Emit(OpCodes.Ldc_I4, (sbyte)Value); } else if (Types.IsSubclass(Value, typeof(Variable))) { // Is parent a type of methodoperation or propertyoperation? // If it is, IsMemberAccessor is true. ((Variable)Value).OutputIL(into, (ParentFragment != null && ParentFragment.IsMemberAccessor())); } else { Error("Didn't know how to output value " + Value + " (type is " + type + ")"); } }