void WriteEnumCore(FileWriter writer, PartialEnumFileInfo info, EnumType enumType) { docWriter.WriteSummary(writer, enumType.Documentation, enumType.RawName); var enumTypeName = enumType.Name(idConverter); foreach (var attr in info.Attributes) { writer.WriteLine(attr); } if (enumType.IsPublic && enumType.IsMissingDocs) { writer.WriteLine(RustConstants.AttributeAllowMissingDocs); } if (!enumType.IsPublic) { writer.WriteLine(RustConstants.AttributeAllowDeadCode); } var pub = enumType.IsPublic ? "pub " : "pub(crate) "; writer.WriteLine($"{pub}enum {enumTypeName} {{"); using (writer.Indent()) { uint expectedValue = 0; foreach (var value in enumType.Values) { docWriter.WriteSummary(writer, value.Documentation, enumType.RawName); if (expectedValue != value.Value) { writer.WriteLine($"{value.Name(idConverter)} = {value.Value},"); } else { writer.WriteLine($"{value.Name(idConverter)},"); } expectedValue = value.Value + 1; } } writer.WriteLine("}"); var arrayName = idConverter.Constant("GenDebug" + enumType.RawName); var feature = info.Attributes.FirstOrDefault(a => a.StartsWith(RustConstants.FeaturePrefix) && a.Contains("(feature")); if (!(feature is null)) { writer.WriteLine(feature); } writer.WriteLine(RustConstants.AttributeNoRustFmt); writer.WriteLine($"static {arrayName}: [&str; {enumType.Values.Length}] = ["); using (writer.Indent()) { for (int i = 0; i < enumType.Values.Length; i++) { writer.WriteLine($"\"{enumType.Values[i].Name(idConverter)}\","); } } writer.WriteLine("];"); // #[derive(Debug)] isn't used since it generates a big switch statement. This code // uses a simple array lookup which has very little code. For small enums the default // implementation might be better though. if (!(feature is null)) { writer.WriteLine(feature); } writer.WriteLine($"impl fmt::Debug for {enumTypeName} {{"); using (writer.Indent()) { writer.WriteLine(RustConstants.AttributeInline); writer.WriteLine($"fn fmt<'a>(&self, f: &mut fmt::Formatter<'a>) -> fmt::Result {{"); using (writer.Indent()) { writer.WriteLine($"write!(f, \"{{}}\", {arrayName}[*self as usize])?;"); writer.WriteLine("Ok(())"); } writer.WriteLine("}"); } writer.WriteLine("}"); if (!(feature is null)) { writer.WriteLine(feature); } writer.WriteLine($"impl Default for {enumTypeName} {{"); using (writer.Indent()) { writer.WriteLine(RustConstants.AttributeMustUse); writer.WriteLine(RustConstants.AttributeInline); writer.WriteLine("fn default() -> Self {"); using (writer.Indent()) { var defaultValue = enumType.Values[0]; // The first one should always have value 0 (eg. "None" field), and if the first one doesn't // have value 0, there must be no other enum fields == 0. if (defaultValue.Value != 0 && enumType.Values.Any(a => a.Value == 0)) { throw new InvalidOperationException(); } writer.WriteLine($"{enumTypeName}::{defaultValue.Name(idConverter)}"); } writer.WriteLine("}"); } writer.WriteLine("}"); }
void GenerateIntel(MemorySizeDef[] defs, Dictionary <string, string> fmtConsts1, Dictionary <string, string[]> fmtConsts2) { var broadcastToKindValues = genTypes[TypeIds.BroadcastToKind].Values; var filename = Path.Combine(generatorContext.Types.Dirs.RustDir, "formatter", "intel", "mem_size_tbl.rs"); var intelKeywords = genTypes[TypeIds.IntelMemoryKeywords].Values; const int BroadcastToKindShift = 5; const int MemoryKeywordsMask = 0x1F; new FileUpdater(TargetLanguage.Rust, "ConstData", filename).Generate(writer => { writer.WriteLine($"const {idConverter.Constant(nameof(BroadcastToKindShift))}: u32 = {BroadcastToKindShift};"); writer.WriteLine($"const {idConverter.Constant(nameof(MemoryKeywordsMask))}: u8 = {MemoryKeywordsMask};"); }); new FileUpdater(TargetLanguage.Rust, "MemorySizes", filename).Generate(writer => { writer.WriteLine(RustConstants.AttributeNoRustFmt); writer.WriteLine($"static MEM_SIZE_TBL_DATA: [u8; {defs.Length}] = ["); using (writer.Indent()) { foreach (var def in defs) { uint value = def.Intel.Value | (def.BroadcastToKind.Value << BroadcastToKindShift); if (value > 0xFF || def.Intel.Value > MemoryKeywordsMask) { throw new InvalidOperationException(); } writer.WriteByte(checked ((byte)value)); writer.WriteLine(); } } writer.WriteLine("];"); }); new FileUpdater(TargetLanguage.Rust, "MemoryKeywordsMatch", filename).Generate(writer => { foreach (var kw in intelKeywords) { writer.Write($"0x{kw.Value:X2} => "); if ((IntelMemoryKeywords)kw.Value == IntelMemoryKeywords.None) { writer.WriteLine("&ac.nothing,"); } else { AddKeywords(fmtConsts1, fmtConsts2, kw.RawName); writer.WriteLine($"&ac.{kw.RawName},"); } } }); new FileUpdater(TargetLanguage.Rust, "BroadcastToKindMatch", filename).Generate(writer => { foreach (var kw in broadcastToKindValues) { writer.Write($"0x{kw.Value:X2} => "); var bcst = (BroadcastToKind)kw.Value; if (bcst == BroadcastToKind.None) { writer.WriteLine("&c.empty,"); } else { Add(fmtConsts1, bcst); writer.WriteLine($"&c.{kw.RawName},"); } } }); }
void GenerateIntel(MemorySizeDef[] defs) { var broadcastToKindValues = genTypes[TypeIds.BroadcastToKind].Values; var filename = CSharpConstants.GetFilename(genTypes, CSharpConstants.IntelFormatterNamespace, "MemorySizes.cs"); var intelKeywords = genTypes[TypeIds.IntelMemoryKeywords].Values; const int BroadcastToKindShift = 5; const int MemoryKeywordsMask = 0x1F; new FileUpdater(TargetLanguage.CSharp, "ConstData", filename).Generate(writer => { writer.WriteLine($"const int {idConverter.Constant(nameof(BroadcastToKindShift))} = {BroadcastToKindShift};"); writer.WriteLine($"const int {idConverter.Constant(nameof(MemoryKeywordsMask))} = {MemoryKeywordsMask};"); var created = new HashSet <string>(StringComparer.Ordinal); foreach (var keywords in intelKeywords.Select(a => a.RawName)) { if (keywords == nameof(IntelMemoryKeywords.None)) { continue; } var parts = keywords.Split('_'); foreach (var kw in parts) { if (created.Add(kw)) { writer.WriteLine($"var {EscapeKeyword(kw)} = new FormatterString(\"{kw}\");"); } } writer.WriteLine($"var {keywords} = new[] {{ {string.Join(", ", parts.Select(a => EscapeKeyword(a)))} }};"); } foreach (var bcst in broadcastToKindValues) { if ((BroadcastToKind)bcst.Value == BroadcastToKind.None) { writer.WriteLine($"var empty = new FormatterString(\"\");"); } else { var name = bcst.RawName; if (!name.StartsWith("b", StringComparison.Ordinal)) { throw new InvalidOperationException(); } var s = name.Substring(1); writer.WriteLine($"var {name} = new FormatterString(\"{s}\");"); } } }); new FileUpdater(TargetLanguage.CSharp, "MemorySizes", filename).Generate(writer => { foreach (var def in defs) { uint value = def.Intel.Value | (def.BroadcastToKind.Value << BroadcastToKindShift); if (value > 0xFF || def.Intel.Value > MemoryKeywordsMask) { throw new InvalidOperationException(); } writer.WriteByte(checked ((byte)value)); writer.WriteLine(); } }); new FileUpdater(TargetLanguage.CSharp, "MemoryKeywordsSwitch", filename).Generate(writer => { foreach (var kw in intelKeywords) { writer.Write($"0x{kw.Value:X2} => "); if ((IntelMemoryKeywords)kw.Value == IntelMemoryKeywords.None) { writer.WriteLine("Array2.Empty<FormatterString>(),"); } else { writer.WriteLine($"{kw.RawName},"); } } }); new FileUpdater(TargetLanguage.CSharp, "BroadcastToKindSwitch", filename).Generate(writer => { foreach (var bcst in broadcastToKindValues) { writer.Write($"0x{bcst.Value:X2} => "); if ((BroadcastToKind)bcst.Value == BroadcastToKind.None) { writer.WriteLine("empty,"); } else { writer.WriteLine($"{bcst.RawName},"); } } }); }
void WriteEnumCore(FileWriter writer, PartialEnumFileInfo info, EnumType enumType) { docWriter.WriteSummary(writer, enumType.Documentation, enumType.RawName); var enumTypeName = enumType.Name(idConverter); foreach (var attr in info.Attributes) { writer.WriteLine(attr); } if (enumType.IsPublic && enumType.IsMissingDocs) { writer.WriteLine(RustConstants.AttributeAllowMissingDocs); } if (!enumType.IsPublic) { writer.WriteLine(RustConstants.AttributeAllowDeadCode); } var pub = enumType.IsPublic ? "pub " : "pub(crate) "; writer.WriteLine($"{pub}enum {enumTypeName} {{"); // Identical enum values aren't allowed so just remove them var enumValues = enumType.Values.Where(a => !a.DeprecatedInfo.IsDeprecatedAndRenamed).ToArray(); using (writer.Indent()) { uint expectedValue = 0; foreach (var value in enumValues) { docWriter.WriteSummary(writer, value.Documentation, enumType.RawName); deprecatedWriter.WriteDeprecated(writer, value); if (expectedValue != value.Value || enumType.IsPublic) { writer.WriteLine($"{value.Name(idConverter)} = {value.Value},"); } else { writer.WriteLine($"{value.Name(idConverter)},"); } expectedValue = value.Value + 1; } } writer.WriteLine("}"); var arrayName = idConverter.Constant("GenDebug" + enumType.RawName); var feature = info.Attributes.FirstOrDefault(a => a.StartsWith(RustConstants.FeaturePrefix, StringComparison.Ordinal) && a.Contains("(feature", StringComparison.Ordinal)); if (feature is not null) { writer.WriteLine(feature); } writer.WriteLine(RustConstants.AttributeNoRustFmt); writer.WriteLine($"static {arrayName}: [&str; {enumValues.Length}] = ["); using (writer.Indent()) { foreach (var value in enumValues) { writer.WriteLine($"\"{value.Name(idConverter)}\","); } } writer.WriteLine("];"); // #[derive(Debug)] isn't used since it generates a big switch statement. This code // uses a simple array lookup which has very little code. For small enums the default // implementation might be better though. if (feature is not null) { writer.WriteLine(feature); } writer.WriteLine($"impl fmt::Debug for {enumTypeName} {{"); using (writer.Indent()) { writer.WriteLine(RustConstants.AttributeInline); writer.WriteLine($"fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {{"); using (writer.Indent()) writer.WriteLine($"write!(f, \"{{}}\", {arrayName}[*self as usize])"); writer.WriteLine("}"); } writer.WriteLine("}"); if (feature is not null) { writer.WriteLine(feature); } writer.WriteLine($"impl Default for {enumTypeName} {{"); using (writer.Indent()) { writer.WriteLine(RustConstants.AttributeMustUse); writer.WriteLine(RustConstants.AttributeInline); writer.WriteLine("fn default() -> Self {"); using (writer.Indent()) { var defaultValue = enumValues[0]; // The first one should always have value 0 (eg. "None" field), and if the first one doesn't // have value 0, there must be no other enum fields == 0. if (defaultValue.Value != 0 && enumValues.Any(a => a.Value == 0)) { throw new InvalidOperationException(); } writer.WriteLine($"{enumTypeName}::{defaultValue.Name(idConverter)}"); } writer.WriteLine("}"); } writer.WriteLine("}"); if (enumType.IsPublic) { var enumIterType = $"{enumTypeName}Iterator"; var icedConstValue = "IcedConstants::" + idConverter.Constant(IcedConstants.GetEnumCountName(enumType.TypeId)); var enumUnderlyingType = GetUnderlyingTypeStr(enumType); // Associated method: values() if (feature is not null) { writer.WriteLine(feature); } writer.WriteLine(RustConstants.AttributeNoRustFmt); writer.WriteLine($"impl {enumTypeName} {{"); using (writer.Indent()) { writer.WriteLine($"/// Iterates over all `{enumTypeName}` enum values"); writer.WriteLine("#[inline]"); writer.WriteLine($"pub fn values() -> impl Iterator<Item = {enumTypeName}> + DoubleEndedIterator + ExactSizeIterator + FusedIterator {{"); using (writer.Indent()) { if (enumType.Values.Length == 1) { writer.WriteLine($"static VALUES: [{enumTypeName}; 1] = [{enumTypeName}::{enumType.Values[0].Name(idConverter)}];"); writer.WriteLine($"VALUES.iter().copied()"); } else { writer.WriteLine("// SAFETY: all values 0-max are valid enum values"); writer.WriteLine($"(0..{icedConstValue}).map(|x| unsafe {{ mem::transmute::<{enumUnderlyingType}, {enumTypeName}>(x as {enumUnderlyingType}) }})"); } } writer.WriteLine("}"); } writer.WriteLine("}"); writer.WriteLine("#[test]"); if (feature is not null) { writer.WriteLine(feature); } writer.WriteLine(RustConstants.AttributeNoRustFmt); writer.WriteLine($"fn test_{enumTypeName.ToLowerInvariant()}_values() {{"); using (writer.Indent()) { writer.WriteLine($"let mut iter = {enumTypeName}::values();"); writer.WriteLine($"assert_eq!(iter.size_hint(), ({icedConstValue}, Some({icedConstValue})));"); writer.WriteLine($"assert_eq!(iter.len(), {icedConstValue});"); writer.WriteLine("assert!(iter.next().is_some());"); writer.WriteLine($"assert_eq!(iter.size_hint(), ({icedConstValue} - 1, Some({icedConstValue} - 1)));"); writer.WriteLine($"assert_eq!(iter.len(), {icedConstValue} - 1);"); writer.WriteLine(); writer.WriteLine($"let values: Vec<{enumTypeName}> = {enumTypeName}::values().collect();"); writer.WriteLine($"assert_eq!(values.len(), {icedConstValue});"); writer.WriteLine("for (i, value) in values.into_iter().enumerate() {"); using (writer.Indent()) writer.WriteLine("assert_eq!(i, value as usize);"); writer.WriteLine("}"); writer.WriteLine(); writer.WriteLine($"let values1: Vec<{enumTypeName}> = {enumTypeName}::values().collect();"); writer.WriteLine($"let mut values2: Vec<{enumTypeName}> = {enumTypeName}::values().rev().collect();"); writer.WriteLine("values2.reverse();"); writer.WriteLine("assert_eq!(values1, values2);"); } writer.WriteLine("}"); // impl trait TryFrom var tryFromTypes = new string[] { "usize", //"u32", }; foreach (var tryFromType in tryFromTypes) { var castToFromType = tryFromType == "usize" ? string.Empty : $" as {tryFromType}"; if (feature is not null) { writer.WriteLine(feature); } writer.WriteLine(RustConstants.AttributeNoRustFmt); writer.WriteLine($"impl TryFrom<{tryFromType}> for {enumTypeName} {{"); using (writer.Indent()) { writer.WriteLine("type Error = IcedError;"); writer.WriteLine("#[inline]"); writer.WriteLine($"fn try_from(value: {tryFromType}) -> Result<Self, Self::Error> {{"); using (writer.Indent()) { writer.WriteLine($"if value < {icedConstValue}{castToFromType} {{"); using (writer.Indent()) { if (enumType.Values.Length == 1) { writer.WriteLine($"Ok({enumTypeName}::{enumType.Values[0].Name(idConverter)})"); } else { writer.WriteLine("// SAFETY: all values 0-max are valid enum values"); writer.WriteLine($"Ok(unsafe {{ mem::transmute(value as {enumUnderlyingType}) }})"); } } writer.WriteLine("} else {"); using (writer.Indent()) writer.WriteLine($"Err(IcedError::new(\"Invalid {enumTypeName} value\"))"); writer.WriteLine("}"); } writer.WriteLine("}"); } writer.WriteLine("}"); if (feature is not null) { writer.WriteLine(feature); } writer.WriteLine("#[test]"); writer.WriteLine(RustConstants.AttributeNoRustFmt); writer.WriteLine($"fn test_{enumTypeName.ToLowerInvariant()}_try_from_{tryFromType}() {{"); using (writer.Indent()) { writer.WriteLine($"for value in {enumTypeName}::values() {{"); using (writer.Indent()) { writer.WriteLine($"let converted = <{enumTypeName} as TryFrom<{tryFromType}>>::try_from(value as {tryFromType}).unwrap();"); writer.WriteLine("assert_eq!(converted, value);"); } writer.WriteLine("}"); writer.WriteLine($"assert!(<{enumTypeName} as TryFrom<{tryFromType}>>::try_from({icedConstValue}{castToFromType}).is_err());"); writer.WriteLine($"assert!(<{enumTypeName} as TryFrom<{tryFromType}>>::try_from(core::{tryFromType}::MAX).is_err());"); } writer.WriteLine("}"); } } }
public string Name(IdentifierConverter idConverter) => idConverter.Constant(RawName);