예제 #1
0
        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("}");
        }
예제 #2
0
        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},");
                    }
                }
            });
        }
예제 #3
0
        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},");
                    }
                }
            });
        }
예제 #4
0
        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("}");
                }
            }
        }
예제 #5
0
 public string Name(IdentifierConverter idConverter) => idConverter.Constant(RawName);