public void GenerateOpCodeReader() { var buf = new StringBuilder(); var template = typeof(Generator).Assembly.ReadAllText("Truesight.TextGenerators.Parser.Generator.OpCodeReader.template"); var lines = template.SplitLines(); foreach (var line in lines) { var iof = line.IndexOf("%FILL_MAPS%"); if (iof != -1) { (line.Trim() == "%FILL_MAPS%").AssertTrue(); var indent = line.Substring(0, iof); var oneByteOpCodes = new Dictionary <Byte, OpCode>(); var twoByteOpCodes = new Dictionary <UInt16, OpCode>(); foreach (var opcode in OpCodeReference.AllOpCodes) { if (opcode.Size == 1) { oneByteOpCodes.Add((Byte)(UInt16)opcode.Value, opcode); } else if (opcode.Size == 2) { twoByteOpCodes.Add((UInt16)opcode.Value, opcode); } else { throw AssertionHelper.Fail(); } } var opcodeNames = typeof(OpCodes).GetFields(BF.PublicStatic) .ToDictionary(f => (OpCode)f.GetValue(null), f => f.Name); var oneByteOpCodePrefixes = new HashSet <Byte>(); var firstByteToOpCode = new Dictionary <Byte, OpCode>(); oneByteOpCodes.Keys.ForEach(oneByte => { buf.AppendLine(indent + "// " + oneByteOpCodes[oneByte].Name); var byte_tos = "0x" + oneByte.ToString("x2"); oneByteOpCodePrefixes.Add(oneByte); buf.AppendLine(indent + String.Format("_oneByteOpCodePrefixes.Add(" + "{0});", byte_tos)); firstByteToOpCode.Add(oneByte, oneByteOpCodes[oneByte]); buf.AppendLine(indent + String.Format("_firstByteToOpCode.Add(" + "{0}, OpCodes.{1});", byte_tos, opcodeNames[firstByteToOpCode[oneByte]])); buf.AppendLine(); }); var twoByteOpCodePrefixes = new HashSet <Byte>(); var secondByteToOpCode = new Dictionary <Byte, OpCode>(); twoByteOpCodes.Keys.ForEach((twoBytes, i) => { var firstByte = (Byte)(twoBytes >> 8); var secondByte = (Byte)(twoBytes % (1 << 8)); buf.AppendLine(indent + "// " + twoByteOpCodes[twoBytes].Name); var firstByte_tos = "0x" + firstByte.ToString("x2"); var secondByte_tos = "0x" + secondByte.ToString("x2"); var twoBytes_tos = "0x" + twoBytes.ToString("x4"); twoByteOpCodePrefixes.Add(firstByte); buf.AppendLine(indent + String.Format("_twoByteOpCodePrefixes.Add(" + "{0});", firstByte_tos)); secondByteToOpCode.Add(secondByte, twoByteOpCodes[twoBytes]); buf.AppendLine(indent + String.Format("_secondByteToOpCode.Add(" + "{0}, OpCodes.{1});", secondByte_tos, opcodeNames[twoByteOpCodes[twoBytes]])); if (i != twoByteOpCodes.Keys.Count() - 1) { buf.AppendLine(); } }); // essential for unambiguous reading! oneByteOpCodePrefixes.Intersect(twoByteOpCodePrefixes).AssertEmpty(); // verify consistence (oneByteOpCodePrefixes.Count() == OpCodeReference.AllOpCodes.Where(oc => oc.Size == 1).Count()).AssertTrue(); (twoByteOpCodePrefixes.Count() == 1).AssertTrue(); (firstByteToOpCode.Count() == OpCodeReference.AllOpCodes.Where(oc => oc.Size == 1).Count()).AssertTrue(); (secondByteToOpCode.Count() == OpCodeReference.AllOpCodes.Where(oc => oc.Size == 2).Count()).AssertTrue(); } else { buf.AppendLine(line); } } File.WriteAllText(@"..\..\..\Truesight\Parser\Impl\Reader\OpCodeReader.Reference.cs", buf.ToString()); }
public void GenerateOpCodeClasses() { var globalKb = ILOpsKb.Content; // todo list for the generator // 1) introduce caching for property getters: // * NB! GENERATE IT rather than wrap around in e.g. ILOpBase // 2) same for prefix getters // 3) implement the "no." prefix 0xfe19 (CIL spec mentions it, but OpCodes do not) // 4) support "readonly." prefix for opcodes it's applicable to // first generate enumerations var enumTypes = new[] { typeof(OperatorType), typeof(PredicateType) }; foreach (var t_enum in enumTypes) { // so far we need only enums t_enum.IsEnum.AssertTrue(); Helpers.GenerateIntoClass( @"..\..\..\Truesight\Parser\Api\Ops\" + t_enum.Name + ".cs", "Truesight.Parser.Api.Ops", "public enum " + t_enum.Name, buffer => { var values = Enum.GetValues(t_enum).Cast <Object>(); var s_values = values.Select(v => v.ToInvariantString().Indent().Indent()); buffer.Append(s_values.StringJoin("," + Environment.NewLine)); }); } // second, set up redirections from enums to generated enums var typeRedirections = enumTypes.ToDictionary( t => t.GetCSharpRef(ToCSharpOptions.ForCodegen), t => "Truesight.Parser.Api.Ops." + t.Name); // third, generate the IILOpType enumeration Helpers.GenerateIntoClass( @"..\..\..\Truesight\Parser\Api\IILOpType.cs", "Truesight.Parser.Api", "public enum IILOpType", buffer => buffer.Append(globalKb.Select(fkb => " " + fkb.Name.Capitalize()).StringJoin("," + Environment.NewLine))); // now generate opcode classes foreach (var fkb in globalKb) { var isIgnored = fkb.SubSpecs.Values.Any(kb => kb.Tags.Contains("Ignore")); (isIgnored && fkb.SubSpecs.Count > 1).AssertFalse(); var isPrefix = fkb.SubSpecs.Values.Any(kb => kb.Tags.Contains("Prefix")); (isPrefix && fkb.SubSpecs.Count > 1).AssertFalse(); var className = fkb.Name.Capitalize(); fkb.OpCodes.AssertNotEmpty(); var opCodesComment = "// " + fkb.OpCodes.Select(opcode => opcode.Name).StringJoin(); var opcodesAttribute = String.Format("[{0}({1})]", typeof(OpCodesAttribute).GetCSharpRef(ToCSharpOptions.ForCodegen), fkb.OpCodes.Select(opcode => opcode.GetCSharpByteSequence()).StringJoin()); var debuggerNonUserCodeAttribute = String.Format("[{0}]", typeof(DebuggerNonUserCodeAttribute).GetCSharpRef(ToCSharpOptions.ForCodegen)); var classDeclaration = opCodesComment + Environment.NewLine + " " + opcodesAttribute + Environment.NewLine + " " + debuggerNonUserCodeAttribute + Environment.NewLine + " " + (isIgnored ? "internal" : "public") + " sealed class " + className + " : " + typeof(ILOp).GetCSharpRef(ToCSharpOptions.ForCodegen); Helpers.GenerateIntoClass( @"..\..\..\Truesight\Parser\Api\Ops\" + className + ".cs", "Truesight.Parser.Api.Ops", classDeclaration, buffer => { var fields = fkb.SubSpecs.Values .SelectMany(spec => spec.Fields.Values.Select(f => f.Name)).Distinct().Order() .Select(fname => UniteSubSpecsForField(fkb, fname)).ToArray(); if (fkb.Name == "branch") { fields = fields.Reverse().ToArray(); } var props = fkb.SubSpecs.Values .SelectMany(spec => spec.Props.Values.Select(p => p.Name)).Distinct().Order() .Select(pname => UniteSubSpecsForProp(fkb, pname)).ToArray(); var prefixes = fkb.SubSpecs.Values .SelectMany(spec => spec.Prefixes.Values.Select(p => p.Name)).Distinct().Order() .Select(pname => { var sample = fkb.SubSpecs.Values.First().Prefixes[pname]; fkb.SubSpecs.Values.AssertAll(kb => { var prefix = kb.Prefixes[pname]; (prefix.Name == sample.Name).AssertTrue(); (prefix.Type == sample.Type).AssertTrue(); (prefix.PrefixName == sample.PrefixName).AssertTrue(); (prefix.Getter == sample.Getter).AssertTrue(); (prefix.Setter == sample.Setter).AssertTrue(); // setters ain't supported since they weren't necessary prefix.Setter.AssertNullOrEmpty(); return(true); }); return(sample); }).ToArray(); // 0. Generate OpCodeType buffer.AppendFormat("public override {0} OpType {{ get {{ return {0}.{1}; }} }}".Indent().Indent(), typeof(IILOpType).GetCSharpRef(ToCSharpOptions.ForCodegen), className).AppendLine().AppendLine(); // 1. Field declarations foreach (var field in fields) { field.IsUnsafe.AssertFalse(); buffer.AppendLine(String.Format("private readonly {0} {1};", field.Type.GetCSharpRef(ToCSharpOptions.ForCodegen), field.Name).Indent().Indent()); } if (fields.Any()) { buffer.AppendLine(); } // 2. Constructor (parsing and field init) var auxCtorHeadline = String.Format("internal {0}({1} source, {2} reader)", className, typeof(MethodBody).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(BinaryReader).GetCSharpRef(ToCSharpOptions.ForCodegen)); buffer.AppendLine(auxCtorHeadline.Indent().Indent()); var emptyPrefixes = String.Format("{0}.ToReadOnly({1}.Empty<{2}>())", typeof(EnumerableExtensions).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(Enumerable).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(ILOp).GetCSharpRef(ToCSharpOptions.ForCodegen)); buffer.AppendLine((": this(source, reader, " + emptyPrefixes + ")").Indent().Indent().Indent()); buffer.AppendLine("{".Indent().Indent()); buffer.AppendLine("}".Indent().Indent()); buffer.AppendLine(); var mainCtorHeadline = String.Format("{0}, {1} prefixes)", auxCtorHeadline.Slice(0, -1), typeof(ReadOnlyCollection <ILOp>).GetCSharpRef(ToCSharpOptions.ForCodegen)); buffer.AppendLine(mainCtorHeadline.Indent().Indent()); buffer.Append(": base(source, AssertSupportedOpCode(reader), ".Indent().Indent().Indent()); // note. be wary of offset magic here! buffer.AppendFormat( "({0})reader.BaseStream.Position - " + "{1}.Sum({1}.Select(prefixes ?? {2}, prefix => prefix.Size))", typeof(Int32).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(Enumerable).GetCSharpRef(ToCSharpOptions.ForCodegen), emptyPrefixes); buffer.AppendLine(", prefixes ?? " + emptyPrefixes + ")"); buffer.AppendLine("{".Indent().Indent()); buffer.AppendLine("// this is necessary for further verification".Indent().Indent().Indent()); buffer.AppendLine("var origPos = reader.BaseStream.Position;".Indent().Indent().Indent()); buffer.AppendLine(); fields.ForEach(field => { buffer.AppendLine(("// initializing " + field.Name).Indent().Indent().Indent()); buffer.AppendLine(field.Initializer.Indent().Indent().Indent()); }); buffer.AppendLine("// verify that we've read exactly the amount of bytes we should".Indent().Indent().Indent()); buffer.AppendLine("var bytesRead = reader.BaseStream.Position - origPos;".Indent().Indent().Indent()); // this validation is partially redundant for switch, tho I'm cba to invent something better now buffer.AppendLine(String.Format("{0}.AssertTrue(bytesRead == SizeOfOperand);", typeof(AssertionHelper).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent().Indent()); buffer.AppendLine(); buffer.AppendLine("// now when the initialization is completed verify that we've got only prefixes we support".Indent().Indent().Indent()); buffer.AppendLine(String.Format("{0}.AssertAll(Prefixes, prefix => ".Indent().Indent().Indent(), typeof(AssertionHelper).GetCSharpRef(ToCSharpOptions.ForCodegen))); buffer.AppendLine("{".Indent().Indent().Indent()); var cond_vars = new List <String>(); foreach (var prefix in prefixes) { var var_name = prefix.PrefixName + "_ok"; cond_vars.Add(var_name); buffer.AppendLine(String.Format("var {0} = prefix is {1}{2};".Indent().Indent().Indent().Indent(), var_name, prefix.PrefixName.Capitalize(), prefix.Filter.IsNullOrEmpty() ? "" : " && " + prefix.Filter)); } buffer.AppendLine(String.Format("return {0};".Indent().Indent().Indent().Indent(), cond_vars.Concat("false").StringJoin(" || "))); buffer.AppendLine("});".Indent().Indent().Indent()); buffer.AppendLine("}".Indent().Indent()); buffer.AppendLine(); // 3. OpCode inference buffer.AppendLine(String.Format("private static {0} AssertSupportedOpCode({1} reader)", typeof(OpCode).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(BinaryReader).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent()); buffer.AppendLine("{".Indent().Indent()); buffer.AppendLine(String.Format( "var opcode = {0}.ReadOpCode(reader);", typeof(OpCodeReader).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent().Indent()); buffer.AppendLine(String.Format( "{0}.AssertNotNull(opcode);", typeof(AssertionHelper).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent().Indent()); buffer.AppendLine(opCodesComment.Indent().Indent().Indent()); buffer.AppendLine(String.Format( "{0}.AssertTrue({1}.Contains(new {2}[]{{{3}}}, ({4})opcode.Value.Value));", typeof(AssertionHelper).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(Enumerable).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(UInt16).GetCSharpRef(ToCSharpOptions.ForCodegen), fkb.OpCodes.Select(opcode => opcode.GetCSharpByteSequence()).StringJoin(), typeof(UInt16).GetCSharpRef(ToCSharpOptions.ForCodegen)) .Indent().Indent().Indent()); buffer.AppendLine(); buffer.AppendLine("return opcode.Value;".Indent().Indent().Indent()); buffer.AppendLine("}".Indent().Indent()); buffer.AppendLine(); // 4. SizeOfOperands override (special case for Switch) if (fkb.OpCodes.SingleOrDefault2() == OpCodes.Switch) { buffer.AppendLine(); buffer.AppendLine(String.Format("public override {0} SizeOfOperand", typeof(Int32).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent()); buffer.AppendLine("{".Indent().Indent()); buffer.AppendLine("get".Indent().Indent().Indent()); buffer.AppendLine("{".Indent().Indent().Indent()); buffer.AppendLine(String.Format("return sizeof({0}) + _targetOffsets.Count * sizeof({0});", typeof(Int32).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent().Indent().Indent()); buffer.AppendLine("}".Indent().Indent().Indent()); buffer.AppendLine("}".Indent().Indent()); } // 5. Property declarations if (props.IsNotEmpty()) { buffer.AppendLine(); } foreach (var prop in props) { buffer.AppendLine(String.Format("{0}public {1} {2}", prop.IsUnsafe ? "unsafe " : "", prop.Type.GetCSharpRef(ToCSharpOptions.ForCodegen), prop.Name).Indent().Indent()); buffer.AppendLine("{".Indent().Indent()); buffer.AppendLine("get".Indent().Indent().Indent()); buffer.AppendLine("{".Indent().Indent().Indent()); var getter = prop.Getter.TrimEnd(); buffer.AppendLine(getter.Indent().Indent().Indent().Indent()); prop.Setter.AssertNullOrEmpty(); buffer.AppendLine("}".Indent().Indent().Indent()); buffer.AppendLine("}".Indent().Indent()); buffer.AppendLine(); } // 6. Prefix declarations if (props.IsEmpty() && prefixes.IsNotEmpty()) { buffer.AppendLine(); } foreach (var prefix in prefixes) { buffer.AppendLine(String.Format("{0}public {1} {2}", prefix.IsUnsafe ? "unsafe " : "", prefix.Type.GetCSharpRef(ToCSharpOptions.ForCodegen), prefix.Name).Indent().Indent()); buffer.AppendLine("{".Indent().Indent()); buffer.AppendLine("get".Indent().Indent().Indent()); buffer.AppendLine("{".Indent().Indent().Indent()); prefix.Setter.AssertNullOrEmpty(); if (prefix.Getter == null) { var getter = String.Format( "return {0}.Any({0}.OfType<{1}>(Prefixes));", typeof(Enumerable).GetCSharpRef(ToCSharpOptions.ForCodegen), prefix.PrefixName.Capitalize()); buffer.AppendLine(getter.Indent().Indent().Indent().Indent()); } else { var getter = String.Format( "return {0}.Single({0}.OfType<{1}>(Prefixes)).{2};", typeof(Enumerable).GetCSharpRef(ToCSharpOptions.ForCodegen), prefix.PrefixName.Capitalize(), prefix.Getter); buffer.AppendLine(getter.Indent().Indent().Indent().Indent()); } buffer.AppendLine("}".Indent().Indent().Indent()); buffer.AppendLine("}".Indent().Indent()); buffer.AppendLine(); } // 7. Generate the stringify routine if (props.IsEmpty()) { buffer.AppendLine(); } buffer.AppendLine(String.Format("public override {0} ToString()", typeof(String).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent()); buffer.AppendLine("{".Indent().Indent()); // Part 1. Offset (for non-prefix only) buffer.Append("var offset = ".Indent().Indent().Indent()); if (isPrefix) { buffer.AppendLine("\"\"; // prefixes never get printed in standalone mode so nothing here"); } if (!isPrefix) { buffer.AppendLine("OffsetToString(Offset) + \":\";"); } // Part 2. Prefixes (wrapped in brackets if any) buffer.Append("var prefixSpec = ".Indent().Indent().Indent()); buffer.Append("Prefixes.Count == 0 ? \"\" : (\"[\" + "); buffer.Append(String.Format("{0}.StringJoin(Prefixes)", typeof(EnumerableExtensions).GetCSharpRef(ToCSharpOptions.ForCodegen))); buffer.AppendLine(" + \"]\");"); // Part 3. Name (as simple as that lol) var tos_name = fkb.Name == "operator" ? "OperatorTypeToString(OperatorType)" : ("\"" + fkb.Name + "\""); buffer.AppendLine(("var name = " + tos_name + ";").Indent().Indent().Indent()); var mods = new List <String>(); String operand = null; // Time for some inference before we continue // note. this stuff is hardcoded (see Visualizer.PrintOutILOpsWithProps for more info) if (fkb.Name == "ldc") { mods.Add("Type == null || OpSpec.OpCode.Value == 0xd0 /*ldtoken*/ ? null : TypeToString(Type)"); operand = "ObjectToCSharpLiteral(Value)"; } else if (fkb.Name == "cast") { mods.Add("ExpectsUn ? \"un\" : \"\""); mods.Add("FailsOnOverflow ? \"ovf\" : \"\""); mods.Add(String.Format("{0}.Format(\"{{0}}->{{1}}\", " + "ExpectsRefOrVal ? \"refval\" : (ExpectsRef ? \"ref\" : (ExpectsVal ? \"val\" : \"???\")), " + "YieldsRefOrVal ? \"refval\" : (YieldsRef ? \"ref\" : (YieldsVal ? \"val\" : \"???\"))" + ")", typeof(String).GetCSharpRef(ToCSharpOptions.ForCodegen))); operand = "(Type != null ? TypeToString(Type) : null)"; } else if (fkb.Name == "call") { mods.Add("IsVirtual ? \"virt\" : \"\""); operand = "(Method != null ? MethodBaseToString(Method) : null)"; } else if (fkb.Name == "operator") { mods.Add("ExpectsUn ? \"un\" : \"\""); mods.Add("FailsOnOverflow ? \"ovf\" : \"\""); } else if (fkb.Name == "branch") { mods.Add("ExpectsUn ? \"un\" : \"\""); mods.Add("PredicateTypeToString(PredicateType)"); operand = "OffsetToString(_absoluteTargetOffset)"; } else if (fkb.Name == "new") { operand = "(Ctor != null ? ConstructorInfoToString(Ctor) : (Type != null ? TypeToString(Type) : null))"; } else if (fkb.Name == "ldftn") { mods.Add("IsVirtual ? \"virt\" : \"\""); operand = "(Method != null ? MethodBaseToString(Method) : null)"; } else if (fkb.Name == "compare") { mods.Add("ExpectsUn ? \"un\" : \"\""); mods.Add("PredicateTypeToString(PredicateType)"); } else if (fkb.Name == "switch") { operand = "OffsetsToString(AbsoluteTargetOffsets)"; } else if (fkb.Name == "calli") { operand = "Sig != null ? ByteArrayToString(Sig) : null"; } else if (fkb.Name == "ldarg" || fkb.Name == "ldarga" || fkb.Name == "starg" || fkb.Name == "ldloc" || fkb.Name == "ldloca" || fkb.Name == "stloc") { props.AssertCount(2); var index = props.Single(p => p.Name == "Index"); var other = props.Except(index).Single(); operand = String.Format("{2} != null ? " + "{1}ToString({2}) : " + "{0}.ToString()", index.Name, other.Type.Name, other.Name); } else if (props.Any(p => p.Name.Contains("Token"))) { PropertySpec token, resolved; if (props.Count() == 2) { token = props.Single(p => p.Name.Contains("Token")); resolved = props.Except(token).SingleOrDefault2(); } else { if (fkb.Name == "ldind" || fkb.Name == "stind") { token = props.Single(p => p.Name == "TypeToken"); resolved = props.Single(p => p.Name == "Type"); } else if (fkb.Name == "ldfld" || fkb.Name == "stfld" || fkb.Name == "ldflda") { token = props.Single(p => p.Name == "FieldToken"); resolved = props.Single(p => p.Name == "Field"); } else { throw AssertionHelper.Fail(); } } operand = String.Format("{0}ToString({1})", resolved.Type.Name, resolved.Name); if (!resolved.Type.IsValueType) { operand = String.Format("({1} != null ? {0} : null)", operand, resolved.Name); } } else if (fkb.Name == "initblk" || fkb.Name == "cpblk") { // do nothing } else if (props.IsNotEmpty()) { props.AssertCount(1); operand = String.Format("{0}ToString({1})", props.Single().Type.Name, props.Single().Name); if (!props.Single().Type.IsValueType) { operand = String.Format("({1} != null ? {0} : null)", operand, props.Single().Name); } } // this hack is necessary for token-related ops // not to crash when the module is left unspecified if (props.Any(p => p.Name.Contains("Token"))) { String tokenExpr; if (fkb.Name == "new") { var rawTokenExpr = "(_ctorToken ?? _typeToken).Value"; rawTokenExpr = String.Format("(\"0x\" + {0}.ToString(\"x8\"))", rawTokenExpr); tokenExpr = "(OpSpec.OpCode.Value == 0x8d /*newarr*/ ? \"arr of \" : \"\") + "; tokenExpr = "(" + tokenExpr + rawTokenExpr + ")"; } else if (fkb.Name == "cast") { var token = props.Single(p => p.Name.Contains("Token")); var rawTokenExpr = String.Format("(\"0x\" + {0}.ToString(\"x8\"))", token.Name); tokenExpr = String.Format("(_typeToken == 0 ? {0} : {1})", operand, rawTokenExpr); } else if (fkb.Name == "call") { var calliExpr = "(Signature != null ? ByteArrayToString(Signature) : (\"0x\" + _signatureToken.ToString(\"x8\")))"; var callExpr = "(\"0x\" + _methodToken.ToString(\"x8\"))"; tokenExpr = String.Format("(OpSpec.OpCode.Value == 0x29 /*calli*/ ? {0} : {1})", calliExpr, callExpr); } else { var token = props.Single(p => p.Name.Contains("Token")); tokenExpr = String.Format("(\"0x\" + {0}.ToString(\"x8\"))", token.Name); } operand = String.Format("({0} ?? ({1}))", operand, tokenExpr); } // Part 4. Mods (e.g. Un, Ovf and likes) buffer.AppendLine(String.Format("var mods = new {0}();", typeof(List <String>).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent().Indent()); mods.ForEach(mod => buffer.AppendLine(String.Format( "mods.Add({0});", mod).Indent().Indent().Indent())); buffer.AppendLine(String.Format("var modSpec = {0}.StringJoin({1}.Where(mods, mod => {2}.IsNeitherNullNorEmpty(mod)), \", \");", typeof(EnumerableExtensions).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(Enumerable).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(EnumerableExtensions).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent().Indent()); // Part 5. Operand (something to be displayed near the opcode) buffer.AppendLine(String.Format("var operand = {0};", operand.IsNullOrEmpty() ? "\"\"" : operand).Indent().Indent().Indent()); // Now assemble the stringified view buffer.AppendLine(); buffer.AppendLine("var parts = new []{offset, prefixSpec, name, modSpec, operand};".Indent().Indent().Indent()); buffer.AppendLine(String.Format( "var result = {0}.StringJoin({1}.Where(parts, p => {2}.IsNeitherNullNorEmpty(p)), \" \");", typeof(EnumerableExtensions).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(Enumerable).GetCSharpRef(ToCSharpOptions.ForCodegen), typeof(EnumerableExtensions).GetCSharpRef(ToCSharpOptions.ForCodegen)).Indent().Indent().Indent()); buffer.AppendLine(); buffer.AppendLine("return result;".Indent().Indent().Indent()); buffer.AppendLine("}".Indent().Indent()); // Fixup the last eoln in the class (added by the TextGeneratedIntoClass.template) var eolnLen = Environment.NewLine.Length; if (buffer.ToString(buffer.Length - eolnLen, eolnLen) == Environment.NewLine) { buffer.Remove(buffer.Length - eolnLen, eolnLen); } // Finally, don't forget to redirect from local enums to generated classes typeRedirections.ForEach(redir => buffer.Replace(redir.Key, redir.Value)); }); } }