/// <summary> /// Constructs a method which gets the parameters list for the specified property. /// </summary> /// <param name="sb">The class writer.</param> /// <param name="info">The property info.</param> /// <param name="additionParameterMethods">The list of methods which return addition parameter names.</param> /// <param name="classAddParams">True if the class has additional parameters method.</param> private static void GetParametersMethod( ClassWriter sb, PropertyInfo info, IReadOnlyCollection <string> additionParameterMethods, bool classAddParams) { string VarToString((string Name, string Type) var) { return($"new CompiledVariable({SurroundInQuotes(var.Name)}, typeof({var.Type}))"); } string?matchingAdditionalParamtersMethod = additionParameterMethods.FirstOrDefault(x => x.Equals($"{ClassInfo.AdditionalParamtersMethodPrefix}{info.Name}")); using var braces = sb.AddMethod( new MethodDefinition($"GetParameters{info.Name}") { SummaryDoc = $"Gets all parameters which should be presented to the {info.Name} expression.", ReturnsDoc = "All parameters needed by the expression.", Access = AccessLevel.Public, Type = "IList<CompiledVariable>", }); sb.AppendLine($"List<CompiledVariable> parameters = new List<CompiledVariable> {{ {string.Join(", ", info.Variables.Select(VarToString))} }};"); if (matchingAdditionalParamtersMethod != null) { sb.AppendLine($"parameters.AddRange(this.{matchingAdditionalParamtersMethod}());"); } if (classAddParams) { sb.AppendLine($"parameters.AddRange(this.Combined{ClassInfo.AdditionalParamtersForClassMethod}());"); } // Reassign the parameters to have the reassigned types if needed. sb.AppendLine("List<CompiledVariable> reassignedParameters = new List<CompiledVariable>();"); sb.AppendLine("var reassignments = this.CombinedVariableTypeReassignments();"); sb.AppendLine("foreach (var parm in parameters)"); sb.StartBrace(); sb.AppendLine("if (reassignments.TryGetValue(parm.Name, out Type? newType))"); sb.StartBrace(); sb.AppendLine("reassignedParameters.Add(new CompiledVariable(parm.Name, newType));"); sb.EndBrace(); sb.AppendLine("else"); sb.StartBrace(); sb.AppendLine("reassignedParameters.Add(parm);"); sb.EndBrace(); sb.EndBrace(); sb.AppendLine("return reassignedParameters;"); }
/// <summary> /// Creates the method which activates all Lazy fields so compile everything. /// </summary> /// <param name="sb">The class writer.</param> /// <param name="classInfo">The class info.</param> private static void CompileMethod(ClassWriter sb, ClassInfo classInfo) { using var braces = sb.AddMethod( new MethodDefinition("Compile") { Access = AccessLevel.Public, Override = true, }); sb.AppendLine("if (this.Compiler == null)"); sb.StartBrace(); sb.AppendLine("throw new InvalidOperationException(\"Definition must be attached before Compile is called.\");"); sb.EndBrace(); sb.AppendLine(); sb.AppendLine("base.Compile();"); foreach (PropertyInfo prop in classInfo.CompiledProps) { sb.AppendLine($"_ = this.compiledCondition{prop.Name}?.Value;"); } foreach (PropertyInfo prop in classInfo.CompiledDictionaryProps) { sb.AppendLine($"this.compiledCondition{prop.Name}?.ToList().ForEach(x => _ = x.Value?.Value);"); } foreach (var prop in classInfo.DefinitionProps) { sb.AppendLine($"this.{prop.Name}?.Compile();"); } foreach (var prop in classInfo.DefinitionArrayProps) { sb.AppendLine($"foreach (var value in this.{prop.Name})"); sb.StartBrace(); sb.AppendLine($"value.Compile();"); sb.EndBrace(); sb.AppendLine(); } foreach (var prop in classInfo.DefinitionDictionaryProps) { sb.AppendLine($"foreach (var value in this.{prop.Name})"); sb.StartBrace(); sb.AppendLine($"value.Value.Compile();"); sb.EndBrace(); sb.AppendLine(); } }
/// <summary> /// Creates the method which runs the condition evalulate method. /// </summary> /// <param name="sb">The class writer.</param> /// <param name="info">The property info.</param> /// <param name="additionParameterMethods">The list of additional parameter methods.</param> /// <param name="classAddParams">True if the class has additional parameters method.</param> private static void EvalDictionaryConditionMethod( ClassWriter sb, PropertyInfo info, IReadOnlyCollection <string> additionParameterMethods, bool classAddParams) { string?matchingAdditionalParamtersMethod = additionParameterMethods.FirstOrDefault(x => x.Equals($"{ClassInfo.AdditionalParamtersMethodPrefix}{info.Name}", StringComparison.Ordinal)); List <ParamDef> allParameters = new List <ParamDef>() { new ParamDef("string", "key", "The key, in the condition dictionary, to evaluate."), new ParamDef("Random", "rdm", "The random number generator"), }; allParameters.AddRange(info.Variables.Select(v => new ParamDef($"LegendsGenerator.Contracts.Things.BaseThing", v.Name, "One of the variables which will be passed to the compiled condition.")).ToList()); if (matchingAdditionalParamtersMethod != null || classAddParams) { allParameters.Add(new ParamDef( "IDictionary<string, LegendsGenerator.Contracts.Things.BaseThing>", "additionalParameters", $"Additional parameters as defined by AdditionalParametersForClass and AdditionalParametersFor{info.Name} methods, or the upstream additional parameters.")); } using var braces = sb.AddMethod( new MethodDefinition($"Eval{info.Name}") { SummaryDoc = $"Evaluates the expressions in the {info.Name} property with the given parameters.", ReturnsDoc = "The result of evaluation.", Access = info.Protected ? AccessLevel.Protected : AccessLevel.Public, Type = info.ReturnType, Parameters = allParameters, }); sb.AppendLine($"if (this.Compiler == null || this.compiledCondition{info.Name} == null)"); sb.StartBrace(); sb.AppendLine("throw new InvalidOperationException(\"Definition must be attached before any Eval method is called.\");"); sb.EndBrace(); sb.AppendLine(); sb.AddDictionary( "Dictionary<string, LegendsGenerator.Contracts.Things.BaseThing>", "param", info.Variables.Select(x => x.Name).ToDictionary(x => x, x => x)); if (matchingAdditionalParamtersMethod != null || classAddParams) { sb.AppendLine("foreach(var additionalParameter in additionalParameters)"); sb.StartBrace(); sb.AppendLine("param[additionalParameter.Key] = additionalParameter.Value;"); sb.EndBrace(); sb.AppendLine(); } sb.AppendLine("try"); sb.StartBrace(); sb.AppendLine($"return this.compiledCondition{info.Name}[key].Value.Evaluate(rdm, param);"); sb.EndBrace(); sb.AppendLine("catch (Exception ex)"); sb.StartBrace(); sb.AppendLine($"throw this.GetConditionException($\"{info.Name}{{key}}\", ex);"); sb.EndBrace(); }
/// <summary> /// Adds the attach override to the source file. /// </summary> /// <param name="sb">The class writer.</param> /// <param name="classInfo">The class info.</param> private static void AttachMethod(ClassWriter sb, ClassInfo classInfo) { using var braces = sb.AddMethod( new MethodDefinition("Attach") { Access = AccessLevel.Public, Override = true, Parameters = new ParamDef[] { new ParamDef("IConditionCompiler", "compiler", "The condition compiler."), new ParamDef("BaseDefinition?", "upsteamDefinition", "null", "The upstream definition."), }, }); sb.AppendLine("base.Attach(compiler, upsteamDefinition);"); foreach (var prop in classInfo.CompiledProps) { sb.AppendLine($"if (this.{prop.Name} != null)"); sb.StartBrace(); sb.AppendLine($"this.compiledCondition{prop.Name} = "); sb.AppendLine($" new Lazy<ICompiledCondition<{prop.ReturnType}>>(() => this.CreateCondition<{prop.ReturnType}>(this.{prop.Name}, {prop.AsFormattedText.ToString().ToLower()}, this.{prop.Name}_IsComplex, this.GetParameters{prop.Name}()));"); sb.EndBrace(); } foreach (var prop in classInfo.CompiledDictionaryProps) { sb.AppendLine($"foreach (var entry in this.{prop.Name})"); sb.StartBrace(); sb.AppendLine($"if (this.{prop.Name}[entry.Key] != null)"); sb.StartBrace(); sb.AppendLine($"this.compiledCondition{prop.Name}[entry.Key] = "); sb.AppendLine($" new Lazy<ICompiledCondition<{prop.ReturnType}>>(() => this.CreateCondition<{prop.ReturnType}>(this.{prop.Name}[entry.Key], {prop.AsFormattedText.ToString().ToLower()}, this.{prop.Name}_IsComplex, this.GetParameters{prop.Name}()));"); sb.EndBrace(); sb.EndBrace(); sb.AppendLine(); } foreach (var prop in classInfo.DefinitionProps) { sb.AppendLine($"this.{prop.Name}?.Attach(compiler, this);"); } foreach (var prop in classInfo.DefinitionArrayProps) { sb.AppendLine($"foreach (var value in this.{prop.Name})"); sb.StartBrace(); sb.AppendLine($"value.Attach(compiler, this);"); sb.EndBrace(); sb.AppendLine(); } foreach (var prop in classInfo.DefinitionDictionaryProps) { sb.AppendLine($"foreach (var value in this.{prop.Name})"); sb.StartBrace(); sb.AppendLine($"value.Value.Attach(compiler, this);"); sb.EndBrace(); sb.AppendLine(); } }