diff --git a/SwiftReflector/SwiftInterfaceReflector/SwiftInterfaceReflector.cs b/SwiftReflector/SwiftInterfaceReflector/SwiftInterfaceReflector.cs index 5cfe3d73..4ae5e738 100644 --- a/SwiftReflector/SwiftInterfaceReflector/SwiftInterfaceReflector.cs +++ b/SwiftReflector/SwiftInterfaceReflector/SwiftInterfaceReflector.cs @@ -104,7 +104,6 @@ public class SwiftInterfaceReflector : SwiftInterfaceBaseListener { internal const string kLabel = "Label"; internal const string kLiteral = "Literal"; internal const string kSeparator = "Separator"; - internal const string kVersion = "Version"; internal const string kSublist = "Sublist"; internal const string kValue = "Value"; @@ -994,9 +993,17 @@ XElement ToAttributeParameter (Balanced_tokenContext context) return literal; } - if (context.Platform_name_platform_version () != null) { - var version = new XElement (kAttribute, new XAttribute (kKind, kVersion), - new XAttribute (kValue, context.Platform_name_platform_version ().GetText ())); + // make the operator look like a label + if (context.@operator () != null) { + var label = new XElement (kAttributeParameter, new XAttribute (kKind, kLabel), + new XAttribute (kValue, context.@operator ().GetText ())); + return label; + } + + if (context.any_punctuation_for_balanced_token () != null) { + var label = new XElement (kAttributeParameter, new XAttribute (kKind, kLabel), + new XAttribute (kValue, context.any_punctuation_for_balanced_token ().GetText ())); + return label; } return null; diff --git a/SwiftReflector/SwiftReflector.csproj b/SwiftReflector/SwiftReflector.csproj index 28be1813..2ea531fc 100644 --- a/SwiftReflector/SwiftReflector.csproj +++ b/SwiftReflector/SwiftReflector.csproj @@ -180,6 +180,7 @@ + diff --git a/SwiftReflector/SwiftXmlReflection/AttributeDeclaration.cs b/SwiftReflector/SwiftXmlReflection/AttributeDeclaration.cs new file mode 100644 index 00000000..e5b5088c --- /dev/null +++ b/SwiftReflector/SwiftXmlReflection/AttributeDeclaration.cs @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using Dynamo; +using System.Linq; + +namespace SwiftReflector.SwiftXmlReflection { + public class AttributeDeclaration { + public AttributeDeclaration (string name) + { + Name = Exceptions.ThrowOnNull (name, nameof (name)); + Parameters = new List (); + } + + public AttributeDeclaration (AttributeDeclaration other) + : this (other.Name) + { + foreach (var parameter in other.Parameters) + Parameters.Add (DuplicateOf (parameter)); + } + + public static AttributeDeclaration FromXElement (XElement elem) + { + var decl = new AttributeDeclaration (elem.Attribute ("name").Value); + var parameters = elem.Element ("attributeparameterlist"); + if (parameters == null) + return decl; + FromAttributeParameterList (parameters, decl.Parameters); + return decl; + } + + internal static void FromAttributeParameterList (XElement parameters, List outlist) + { + foreach (var parameterElem in parameters.Elements ("attributeparameter")) { + var parameter = AttributeParameter.FromXElement (parameterElem); + outlist.Add (parameter); + } + } + + public static AttributeParameter DuplicateOf(AttributeParameter other) + { + switch (other.Kind) { + case AttributeParameterKind.Label: + return new AttributeParameterLabel ((AttributeParameterLabel)other); + case AttributeParameterKind.Literal: + return new AttributeParameterLiteral ((AttributeParameterLiteral)other); + case AttributeParameterKind.Sublist: + return new AttributeParameterSublist ((AttributeParameterSublist)other); + case AttributeParameterKind.Unknown: + return new AttributeParameter (); + default: + throw new ArgumentOutOfRangeException (nameof (other), other.Kind.ToString ()); + } + } + + public string Name { get; private set; } + public List Parameters { get; private set; } + } + + public class AttributeParameter { + public static AttributeParameter FromXElement (XElement elem) + { + switch (elem.Attribute ("kind").Value) { + case "Label": + return new AttributeParameterLabel (elem.Attribute ("Value").Value); + case "Literal": + return new AttributeParameterLiteral (elem.Attribute ("Value").Value); + case "Sublist": + return AttributeParameterSublist.SublistFromXElement (elem); + default: + return new AttributeParameter (); + } + } + + public virtual AttributeParameterKind Kind => AttributeParameterKind.Unknown; + } + + public class AttributeParameterLabel : AttributeParameter { + public AttributeParameterLabel (string label) + { + Label = Exceptions.ThrowOnNull (label, nameof (label)); + } + + public AttributeParameterLabel (AttributeParameterLabel other) + : this (other.Label) + { + } + + public override AttributeParameterKind Kind => AttributeParameterKind.Label; + public string Label { get; private set; } + } + + public class AttributeParameterLiteral : AttributeParameter { + public AttributeParameterLiteral (string literal) + { + Literal = Exceptions.ThrowOnNull (literal, nameof (literal)); + } + + public AttributeParameterLiteral (AttributeParameterLiteral other) + : this (other.Literal) + { + } + + public override AttributeParameterKind Kind => AttributeParameterKind.Literal; + public string Literal { get; private set; } + } + + public class AttributeParameterSublist : AttributeParameter { + public AttributeParameterSublist () + { + Parameters = new List (); + } + + public AttributeParameterSublist (AttributeParameterSublist other) + : this () + { + Parameters.AddRange (other.Parameters.Select (prm => AttributeDeclaration.DuplicateOf (prm))); + } + + public static AttributeParameterSublist SublistFromXElement (XElement elem) + { + var sublist = new AttributeParameterSublist (); + var parameters = elem.Element ("attributeparameterlist"); + if (parameters == null) + return sublist; + AttributeDeclaration.FromAttributeParameterList (parameters, sublist.Parameters); + return sublist; + } + + public override AttributeParameterKind Kind => AttributeParameterKind.Sublist; + public List Parameters { get; private set; } + } +} diff --git a/SwiftReflector/SwiftXmlReflection/BaseDeclaration.cs b/SwiftReflector/SwiftXmlReflection/BaseDeclaration.cs index ff6c60f6..7cfbbfb2 100644 --- a/SwiftReflector/SwiftXmlReflection/BaseDeclaration.cs +++ b/SwiftReflector/SwiftXmlReflection/BaseDeclaration.cs @@ -15,6 +15,7 @@ public class BaseDeclaration { protected BaseDeclaration () { Generics = new GenericDeclarationCollection (); + Attributes = new List (); } protected BaseDeclaration (BaseDeclaration other) @@ -25,6 +26,7 @@ protected BaseDeclaration (BaseDeclaration other) Parent = other.Parent; ParentExtension = other.ParentExtension; Generics = new GenericDeclarationCollection (); + Attributes = new List (); } public string Name { get; set; } @@ -39,6 +41,7 @@ public bool ContainsGenericParameters { return Generics.Count () > 0; } } + public List Attributes { get; private set; } public bool IsTypeSpecBoundGeneric (TypeSpec sp) { @@ -499,9 +502,17 @@ public static BaseDeclaration FromXElement (XElement elem, ModuleDeclaration mod break; } decl.Generics.AddRange (generics); + decl.Attributes.AddRange (AttributesFromXElement (elem.Element ("attributes"))); return decl; } + internal static IEnumerable AttributesFromXElement (XElement elem) + { + if (elem == null) + return Enumerable.Empty (); + return elem.Elements ("attribute").Select (attr => AttributeDeclaration.FromXElement (attr)); + } + public virtual string ToFullyQualifiedName (bool includeModule = true) { var sb = new StringBuilder (); diff --git a/SwiftReflector/SwiftXmlReflection/Enums.cs b/SwiftReflector/SwiftXmlReflection/Enums.cs index 0e86fc71..19a2909b 100644 --- a/SwiftReflector/SwiftXmlReflection/Enums.cs +++ b/SwiftReflector/SwiftXmlReflection/Enums.cs @@ -70,5 +70,13 @@ public enum ConstraintKind { Inherits, Equal } + + public enum AttributeParameterKind { + None, + Label, + Literal, + Sublist, + Unknown, + } } diff --git a/swiftinterfaceparser/SwiftInterface.g4 b/swiftinterfaceparser/SwiftInterface.g4 index 2c71088c..4c55b3b6 100644 --- a/swiftinterfaceparser/SwiftInterface.g4 +++ b/swiftinterfaceparser/SwiftInterface.g4 @@ -291,7 +291,7 @@ balanced_token : | OpLBrace balanced_tokens OpRBrace | label_identifier | literal - | Platform_name_platform_version + | operator | any_punctuation_for_balanced_token ; @@ -465,23 +465,6 @@ fragment Identifier_characters : Identifier_character+ ; Implicit_parameter_name : '$' Decimal_digits ; -Platform_name_platform_version : Platform_name WS Platform_version ; - -fragment Platform_name : - 'iOS' - | 'iOSApplicationExtension' - | 'macOS' - | 'macOSApplicationExtension' - | 'watchOS' - | 'tvOS' - ; - -fragment Platform_version : - Pure_decimal_digits - | Pure_decimal_digits OpDot Pure_decimal_digits - | Pure_decimal_digits OpDot Pure_decimal_digits OpDot Pure_decimal_digits - ; - generic_parameter_clause : OpLess generic_parameter_list OpGreater ; generic_parameter_list : generic_parameter (OpComma generic_parameter)* ; generic_parameter : type_name diff --git a/tests/tom-swifty-test/XmlReflectionTests/SwiftInterfaceParserTests.cs b/tests/tom-swifty-test/XmlReflectionTests/SwiftInterfaceParserTests.cs index 8ab5c626..ef122d17 100644 --- a/tests/tom-swifty-test/XmlReflectionTests/SwiftInterfaceParserTests.cs +++ b/tests/tom-swifty-test/XmlReflectionTests/SwiftInterfaceParserTests.cs @@ -303,5 +303,102 @@ public override init () { } .Where (el => el.Attribute ("name").Value == "objc").FirstOrDefault (); Assert.IsNotNull (attribute, "no function attribute"); } + + [Test] + public void HasAttributeDeclarations () + { + var swiftCode = @" +import Foundation +@objc +public class Foo : NSObject { + public override init () { } +} +"; + SwiftInterfaceReflector reflector; + var module = ReflectToModules (swiftCode, "SomeModule", out reflector).FirstOrDefault (mod => mod.Name == "SomeModule"); + Assert.IsNotNull (module, "no module"); + + var cl = module.Classes.FirstOrDefault (c => c.Name == "Foo"); + Assert.IsNotNull (cl, "no class"); + + Assert.AreEqual (2, cl.Attributes.Count, "wrong number of attributes"); + + var attr = cl.Attributes.FirstOrDefault (at => at.Name == "objc"); + Assert.IsNotNull (attr, "no objc attribute"); + } + + + [Test] + public void HasAttributeObjCSelectorParameter () + { + var swiftCode = @" +import Foundation +@objc +public class Foo : NSObject { + public override init () { } + @objc(narwhal) + public func DoSomething () -> Int { + return 1 + } +} +"; + SwiftInterfaceReflector reflector; + var module = ReflectToModules (swiftCode, "SomeModule", out reflector).FirstOrDefault (mod => mod.Name == "SomeModule"); + Assert.IsNotNull (module, "no module"); + + var cl = module.Classes.FirstOrDefault (c => c.Name == "Foo"); + Assert.IsNotNull (cl, "no class"); + + var method = cl.Members.OfType ().FirstOrDefault (fn => fn.Name == "DoSomething"); + Assert.IsNotNull (method, "no method"); + + Assert.AreEqual (1, method.Attributes.Count, "wrong number of attributes"); + + var attr = method.Attributes.FirstOrDefault (at => at.Name == "objc"); + Assert.IsNotNull (attr, "no objc attribute"); + var attrParam = attr.Parameters [0] as AttributeParameterLabel; + Assert.IsNotNull (attrParam, "not a label"); + Assert.AreEqual (attrParam.Label, "narwhal", "wrong label"); + } + + [Test] + public void HasAvailableAttributeAll () + { + var swiftCode = @" +import Foundation + + +public class Foo { + public init () { } + @available (*, unavailable) + public func DoSomething () -> Int { + return 1 + } +} +"; + SwiftInterfaceReflector reflector; + var module = ReflectToModules (swiftCode, "SomeModule", out reflector).FirstOrDefault (mod => mod.Name == "SomeModule"); + Assert.IsNotNull (module, "no module"); + + var cl = module.Classes.FirstOrDefault (c => c.Name == "Foo"); + Assert.IsNotNull (cl, "no class"); + + var method = cl.Members.OfType ().FirstOrDefault (fn => fn.Name == "DoSomething"); + Assert.IsNotNull (method, "no method"); + + Assert.AreEqual (1, method.Attributes.Count, "wrong number of attributes"); + var attr = method.Attributes [0]; + Assert.AreEqual (attr.Name, "available"); + Assert.AreEqual (3, attr.Parameters.Count, "wrong number of parameters"); + var label = attr.Parameters [0] as AttributeParameterLabel; + Assert.IsNotNull (label, "not a label at 0"); + Assert.AreEqual ("*", label.Label, "not a star"); + label = attr.Parameters [1] as AttributeParameterLabel; + Assert.IsNotNull (label, "not a label at 1"); + Assert.AreEqual (",", label.Label, "not a comma"); + label = attr.Parameters [2] as AttributeParameterLabel; + Assert.IsNotNull (label, "not a label at 2"); + Assert.AreEqual ("unavailable", label.Label, "not unavailable"); + } } }