From 488679fcd3d75f6704dff9fe2f1cb5d8a8553797 Mon Sep 17 00:00:00 2001 From: zhouqili Date: Mon, 8 Apr 2024 19:44:20 +0800 Subject: [PATCH 1/2] object's attributes are parsed and saved in MetaAttributeMap. remove unnecessary code. --- src/CppAst/CppAst.csproj | 1 + src/CppAst/CppClass.cs | 2 + src/CppAst/CppEnum.cs | 2 + src/CppAst/CppEnumItem.cs | 2 + src/CppAst/CppField.cs | 4 +- src/CppAst/CppFunction.cs | 2 + src/CppAst/CppGlobalDeclarationContainer.cs | 2 + src/CppAst/CppModelBuilder.cs | 107 ++++- src/CppAst/CppNamespace.cs | 1 + src/CppAst/CppTypedef.cs | 13 +- src/CppAst/ICppAttributeContainer.cs | 2 + src/CppAst/attribute/CustomAttributeTool.cs | 208 ++++++++++ src/CppAst/attribute/NamedParamerterParser.cs | 365 ++++++++++++++++++ 13 files changed, 695 insertions(+), 16 deletions(-) create mode 100644 src/CppAst/attribute/CustomAttributeTool.cs create mode 100644 src/CppAst/attribute/NamedParamerterParser.cs diff --git a/src/CppAst/CppAst.csproj b/src/CppAst/CppAst.csproj index bc8de38..1f28542 100644 --- a/src/CppAst/CppAst.csproj +++ b/src/CppAst/CppAst.csproj @@ -31,6 +31,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/CppAst/CppClass.cs b/src/CppAst/CppClass.cs index a359306..af984e7 100644 --- a/src/CppAst/CppClass.cs +++ b/src/CppAst/CppClass.cs @@ -105,6 +105,8 @@ public override string FullName [Obsolete("TokenAttributes is deprecated. please use system attributes and annotate attributes")] public List TokenAttributes { get; } + public MetaAttributeMap MetaAttributes { get; private set; } = new MetaAttributeMap(); + /// /// Gets or sets a boolean indicating if this type is a definition. If false the type was only declared but is not defined. /// diff --git a/src/CppAst/CppEnum.cs b/src/CppAst/CppEnum.cs index c5715de..a4dec96 100644 --- a/src/CppAst/CppEnum.cs +++ b/src/CppAst/CppEnum.cs @@ -72,6 +72,8 @@ public override string FullName [Obsolete("TokenAttributes is deprecated. please use system attributes and annotate attributes")] public List TokenAttributes { get; } + public MetaAttributeMap MetaAttributes { get; private set; } = new MetaAttributeMap(); + private bool Equals(CppEnum other) { return base.Equals(other) && Equals(Parent, other.Parent) && Equals(Name, other.Name); diff --git a/src/CppAst/CppEnumItem.cs b/src/CppAst/CppEnumItem.cs index 8095ea5..38c3197 100644 --- a/src/CppAst/CppEnumItem.cs +++ b/src/CppAst/CppEnumItem.cs @@ -42,6 +42,8 @@ public CppEnumItem(string name, long value) [Obsolete("TokenAttributes is deprecated. please use system attributes and annotate attributes")] public List TokenAttributes { get; } = new List(); + public MetaAttributeMap MetaAttributes { get; private set; } = new MetaAttributeMap(); + /// public override string ToString() { diff --git a/src/CppAst/CppField.cs b/src/CppAst/CppField.cs index 830c541..1958c6a 100644 --- a/src/CppAst/CppField.cs +++ b/src/CppAst/CppField.cs @@ -11,7 +11,7 @@ namespace CppAst /// /// A C++ field (of a struct/class) or global variable. /// - public sealed class CppField : CppDeclaration, ICppMemberWithVisibility , ICppAttributeContainer + public sealed class CppField : CppDeclaration, ICppMemberWithVisibility, ICppAttributeContainer { public CppField(CppType type, string name) { @@ -37,6 +37,8 @@ public CppField(CppType type, string name) [Obsolete("TokenAttributes is deprecated. please use system attributes and annotate attributes")] public List TokenAttributes { get; } + public MetaAttributeMap MetaAttributes { get; private set; } = new MetaAttributeMap(); + /// /// Gets the type of this field/variable. /// diff --git a/src/CppAst/CppFunction.cs b/src/CppAst/CppFunction.cs index 07e76a3..91b2058 100644 --- a/src/CppAst/CppFunction.cs +++ b/src/CppAst/CppFunction.cs @@ -42,6 +42,8 @@ public CppFunction(string name) [Obsolete("TokenAttributes is deprecated. please use system attributes and annotate attributes")] public List TokenAttributes { get; } + public MetaAttributeMap MetaAttributes { get;} = new MetaAttributeMap(); + /// /// Gets or sets the storage qualifier. /// diff --git a/src/CppAst/CppGlobalDeclarationContainer.cs b/src/CppAst/CppGlobalDeclarationContainer.cs index 6f5060d..a8d4fa0 100644 --- a/src/CppAst/CppGlobalDeclarationContainer.cs +++ b/src/CppAst/CppGlobalDeclarationContainer.cs @@ -66,6 +66,8 @@ public CppGlobalDeclarationContainer() [Obsolete("TokenAttributes is deprecated. please use system attributes and annotate attributes")] public List TokenAttributes { get; } + public MetaAttributeMap MetaAttributes { get; private set; } = new MetaAttributeMap(); + /// public virtual IEnumerable Children() { diff --git a/src/CppAst/CppModelBuilder.cs b/src/CppAst/CppModelBuilder.cs index c72cbdf..8307f44 100644 --- a/src/CppAst/CppModelBuilder.cs +++ b/src/CppAst/CppModelBuilder.cs @@ -125,7 +125,8 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi Debug.Assert(parent != null); var cppEnum = new CppEnum(GetCursorSpelling(cursor)) { - IsAnonymous = cursor.IsAnonymous + IsAnonymous = cursor.IsAnonymous, + Visibility = GetVisibility(cursor.CXXAccessSpecifier) }; parentDeclarationContainer.Enums.Add(cppEnum); symbol = cppEnum; @@ -433,7 +434,10 @@ private CXChildVisitResult VisitMember(CXCursor cursor, CXCursor parent, void* d if (element != null) { - AssignSourceSpan(cursor, element); + bool isForwardDeclaration = (element is CppClass || element is CppEnum) && !cursor.IsDefinition; + if (!isForwardDeclaration) { + AssignSourceSpan(cursor, element); + } } if (element is ICppDeclaration cppDeclaration) @@ -448,6 +452,11 @@ private CXChildVisitResult VisitMember(CXCursor cursor, CXCursor parent, void* d } } + if (element is ICppAttributeContainer container) + { + TryToConvertAttributesToMetaAttributes(container); + } + return CXChildVisitResult.CXChildVisit_Continue; } @@ -807,8 +816,10 @@ private static CppVisibility GetVisibility(CX_CXXAccessSpecifier accessSpecifier return CppVisibility.Protected; case CX_CXXAccessSpecifier.CX_CXXPrivate: return CppVisibility.Private; - default: + case CX_CXXAccessSpecifier.CX_CXXPublic: return CppVisibility.Public; + default: + return CppVisibility.Default; } } @@ -816,7 +827,8 @@ private static void AssignSourceSpan(CXCursor cursor, CppElement element) { var start = cursor.Extent.Start; var end = cursor.Extent.End; - element.Span = new CppSourceSpan(GetSourceLocation(start), GetSourceLocation(end)); + if (element.Span.Start.File is null) + element.Span = new CppSourceSpan(GetSourceLocation(start), GetSourceLocation(end)); } public static CppSourceLocation GetSourceLocation(CXSourceLocation start) @@ -844,7 +856,7 @@ private CppField VisitFieldOrVariable(CppContainerContext containerContext, CXCu { cppField = new CppField(type, fieldName) { - Visibility = containerContext.CurrentVisibility, + Visibility = GetVisibility(cursor.CXXAccessSpecifier), StorageQualifier = GetStorageQualifier(cursor), IsBitField = cursor.IsBitField, BitFieldWidth = cursor.FieldDeclBitWidth, @@ -869,7 +881,7 @@ private void AddAnonymousTypeWithField(CppContainerContext containerContext, CXC var fieldName = "__anonymous__" + containerContext.DeclarationContainer.Fields.Count; var cppField = new CppField(fieldType, fieldName) { - Visibility = containerContext.CurrentVisibility, + Visibility = GetVisibility(cursor.CXXAccessSpecifier), StorageQualifier = GetStorageQualifier(cursor), IsAnonymous = true, Offset = cursor.OffsetOfField / 8, @@ -1218,7 +1230,9 @@ private CppFunction VisitFunctionDecl(CXCursor cursor, CXCursor parent, void* da //We need ignore the function define out in the class definition here(Otherwise it will has two same functions here~)! var semKind = cursor.SemanticParent.Kind; - if ((semKind == CXCursorKind.CXCursor_StructDecl || semKind == CXCursorKind.CXCursor_ClassDecl) + if ((semKind == CXCursorKind.CXCursor_StructDecl || + semKind == CXCursorKind.CXCursor_ClassDecl || + semKind == CXCursorKind.CXCursor_ClassTemplate) && cursor.LexicalParent != cursor.SemanticParent) { return null; @@ -1226,7 +1240,7 @@ private CppFunction VisitFunctionDecl(CXCursor cursor, CXCursor parent, void* da var cppFunction = new CppFunction(functionName) { - Visibility = contextContainer.CurrentVisibility, + Visibility = GetVisibility(cursor.CXXAccessSpecifier), StorageQualifier = GetStorageQualifier(cursor), LinkageKind = GetLinkage(cursor.Linkage), }; @@ -1535,6 +1549,53 @@ private void TryToParseAttributesFromComment(CppComment comment, ICppAttributeCo } } } + + private void AppendToMetaAttributes(List metaList, MetaAttribute metaAttr) + { + if (metaAttr is null) + { + return; + } + + foreach (MetaAttribute meta in metaList) + { + foreach (KeyValuePair kvp in meta.ArgumentMap) + { + if (metaAttr.ArgumentMap.ContainsKey(kvp.Key)) + { + metaAttr.ArgumentMap.Remove(kvp.Key); + } + } + } + + if (metaAttr.ArgumentMap.Count > 0) + { + metaList.Add(metaAttr); + } + } + + private void TryToConvertAttributesToMetaAttributes(ICppAttributeContainer attrContainer) + { + foreach (var attr in attrContainer.Attributes) + { + //Now we only handle for annotate attribute here + if (attr.Kind == AttributeKind.AnnotateAttribute) + { + MetaAttribute metaAttr = null; + string errorMessage = null; + + metaAttr = CustomAttributeTool.ParseMetaStringFor(attr.Arguments, out errorMessage); + + if (!string.IsNullOrEmpty(errorMessage)) + { + var element = (CppElement)attrContainer; + throw new Exception($"handle meta not right, detail: `{errorMessage}, location: `{element.Span}`"); + } + + AppendToMetaAttributes(attrContainer.MetaAttributes.MetaList, metaAttr); + } + } + } private void ParseAttributes(CXCursor cursor, ICppAttributeContainer attrContainer, bool needOnlineSeek = false) { @@ -1570,6 +1631,18 @@ private void ParseAttributes(CXCursor cursor, ICppAttributeContainer attrContain attrContainer.TokenAttributes.AddRange(tokenAttributes); } + private void ParseTypedefAttribute(CXCursor cursor, CppType type, CppType underlyingTypeDefType) + { + if (type is CppTypedef typedef) + { + ParseAttributes(cursor, typedef, true); + if (underlyingTypeDefType is CppClass targetClass) + { + targetClass.Attributes.AddRange(typedef.Attributes); + TryToConvertAttributesToMetaAttributes(targetClass); + } + } + } private CppType VisitTypeAliasDecl(CXCursor cursor, void* data) { @@ -1599,11 +1672,13 @@ private CppType VisitTypeAliasDecl(CXCursor cursor, void* data) } else { - var typedef = new CppTypedef(GetCursorSpelling(cursor), underlyingTypeDefType) { Visibility = contextContainer.CurrentVisibility }; + var typedef = new CppTypedef(typedefName, underlyingTypeDefType) { Visibility = contextContainer.CurrentVisibility }; contextContainer.DeclarationContainer.Typedefs.Add(typedef); type = typedef; } + ParseTypedefAttribute(cursor, type, underlyingTypeDefType); + // The type could have been added separately as part of the GetCppType above if (_typedefs.TryGetValue(fulltypeDefName, out var cppPreviousCppType)) { @@ -1626,7 +1701,7 @@ private CppType VisitTypeDefDecl(CXCursor cursor, void* data) var contextContainer = GetOrCreateDeclarationContainer(cursor.SemanticParent, data); var underlyingTypeDefType = GetCppType(cursor.TypedefDeclUnderlyingType.Declaration, cursor.TypedefDeclUnderlyingType, cursor, data); - + var typedefName = GetCursorSpelling(cursor); if (AutoSquashTypedef && underlyingTypeDefType is ICppMember cppMember && (string.IsNullOrEmpty(cppMember.Name) || typedefName == cppMember.Name)) @@ -1636,11 +1711,13 @@ private CppType VisitTypeDefDecl(CXCursor cursor, void* data) } else { - var typedef = new CppTypedef(GetCursorSpelling(cursor), underlyingTypeDefType) { Visibility = contextContainer.CurrentVisibility }; + var typedef = new CppTypedef(typedefName, underlyingTypeDefType) { Visibility = contextContainer.CurrentVisibility }; contextContainer.DeclarationContainer.Typedefs.Add(typedef); type = typedef; } + ParseTypedefAttribute(cursor, type, underlyingTypeDefType); + // The type could have been added separately as part of the GetCppType above if (_typedefs.TryGetValue(fulltypeDefName, out var cppPreviousCppType)) { @@ -1656,8 +1733,7 @@ private CppType VisitTypeDefDecl(CXCursor cursor, void* data) private CppType VisitElaboratedDecl(CXCursor cursor, CXType type, CXCursor parent, void* data) { var fulltypeDefName = clang.getCursorUSR(cursor).CString; - if (_typedefs.TryGetValue(fulltypeDefName, out var typeRef)) - { + if (_typedefs.TryGetValue(fulltypeDefName, out var typeRef)) { return typeRef; } @@ -1696,7 +1772,7 @@ private CppType GetCppType(CXCursor cursor, CXType type, CXCursor parent, void* if (type.IsConstQualified) { // Skip if it is already qualified. - if (cppType is CppQualifiedType q && q.Qualifier == CppTypeQualifier.Const) + if (cppType is CppUnexposedType || (cppType is CppQualifiedType q && q.Qualifier == CppTypeQualifier.Const)) { return cppType; } @@ -1829,6 +1905,9 @@ private CppType GetCppTypeInternal(CXCursor cursor, CXType type, CXCursor parent case CXTypeKind.CXType_Attributed: return GetCppType(type.ModifiedType.Declaration, type.ModifiedType, parent, data); + case CXTypeKind.CXType_Auto: + return GetCppType(type.Declaration, type.Declaration.Type, parent, data); + default: { WarningUnhandled(cursor, parent, type); diff --git a/src/CppAst/CppNamespace.cs b/src/CppAst/CppNamespace.cs index 88349e8..9b7cf89 100644 --- a/src/CppAst/CppNamespace.cs +++ b/src/CppAst/CppNamespace.cs @@ -62,6 +62,7 @@ public CppNamespace(string name) [Obsolete("TokenAttributes is deprecated. please use system attributes and annotate attributes")] public List TokenAttributes { get; } + public MetaAttributeMap MetaAttributes { get; private set; } = new MetaAttributeMap(); protected bool Equals(CppNamespace other) { diff --git a/src/CppAst/CppTypedef.cs b/src/CppAst/CppTypedef.cs index 4aade4c..445319e 100644 --- a/src/CppAst/CppTypedef.cs +++ b/src/CppAst/CppTypedef.cs @@ -3,13 +3,14 @@ // See license.txt file in the project root for full license information. using System; +using System.Collections.Generic; namespace CppAst { /// /// A C++ typedef (e.g `typedef int XXX`) /// - public sealed class CppTypedef : CppTypeDeclaration, ICppMemberWithVisibility + public sealed class CppTypedef : CppTypeDeclaration, ICppMemberWithVisibility, ICppAttributeContainer { /// /// Creates a new instance of a typedef. @@ -20,8 +21,18 @@ public CppTypedef(string name, CppType type) : base(CppTypeKind.Typedef) { Name = name ?? throw new ArgumentNullException(nameof(name)); ElementType = type; + Attributes = new List(); + TokenAttributes = new List(); + MetaAttributes = new MetaAttributeMap(); } + public List Attributes { get; } + + [Obsolete("TokenAttributes is deprecated. please use system attributes and annotate attributes")] + public List TokenAttributes { get; } + + public MetaAttributeMap MetaAttributes { get; private set; } + public CppType ElementType { get; } /// diff --git a/src/CppAst/ICppAttributeContainer.cs b/src/CppAst/ICppAttributeContainer.cs index cc5fa7a..da913f5 100644 --- a/src/CppAst/ICppAttributeContainer.cs +++ b/src/CppAst/ICppAttributeContainer.cs @@ -18,5 +18,7 @@ public interface ICppAttributeContainer [Obsolete("TokenAttributes is deprecated. please use system attributes and annotate attributes")] List TokenAttributes { get; } + + MetaAttributeMap MetaAttributes { get; } } } \ No newline at end of file diff --git a/src/CppAst/attribute/CustomAttributeTool.cs b/src/CppAst/attribute/CustomAttributeTool.cs new file mode 100644 index 0000000..dad6372 --- /dev/null +++ b/src/CppAst/attribute/CustomAttributeTool.cs @@ -0,0 +1,208 @@ +using System.Text; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace CppAst +{ + public class MetaAttribute + { + public string FeatureName; + public Dictionary ArgumentMap = new Dictionary(); + + public override string ToString() + { + var builder = new StringBuilder(); + builder.Append($"{FeatureName} {{"); + foreach ( var kvp in ArgumentMap ) + { + builder.Append( $"{kvp.Key}: {kvp.Value}, "); + } + builder.Append("}"); + return builder.ToString(); + } + + public bool QueryKeyIsTrue(string key) + { + return ArgumentMap.ContainsKey(key) && (((ArgumentMap[key] is bool) && (bool)ArgumentMap[key]) || ((ArgumentMap[key] is string && (string)ArgumentMap[key] == "true"))); + } + + public bool QueryKeysAreTrue(List keys) + { + if (keys == null || !keys.Any()) + { + return false; + } + + foreach (string key in keys) + { + if (!QueryKeyIsTrue(key)) + { + return false; + } + } + + return true; + } + } + + public class MetaAttributeMap + { + public List MetaList { get; private set; } = new List(); + + public bool IsNull + { + get + { + return MetaList.Count == 0; + } + } + + public object QueryArgument(string argName) + { + if (MetaList.Count == 0) return null; + + foreach (var argMap in MetaList) + { + if (argMap.ArgumentMap.ContainsKey(argName)) + { + return argMap.ArgumentMap[argName]; + } + } + + return null; + } + + public bool QueryArgumentAsBool(string argName, bool defaultVal) + { + var obj = QueryArgument(argName); + if (obj != null) + { + try + { + return Convert.ToBoolean(obj); + } + catch(Exception) + { + } + } + + return defaultVal; + } + + public int QueryArgumentAsInteger(string argName, int defaultVal) + { + var obj = QueryArgument(argName); + if (obj != null) + { + try + { + return Convert.ToInt32(obj); + } + catch (Exception) + { + } + } + + return defaultVal; + } + + public string QueryArgumentAsString(string argName, string defaultVal) + { + var obj = QueryArgument(argName); + if (obj != null) + { + try + { + return Convert.ToString(obj); + } + catch (Exception) + { + } + } + + return defaultVal; + } + } + + public static class CustomAttributeTool + { + public const string kMetaLeaderWord = "rmeta"; + public const string kMetaClassLeaderWord = "class"; + public const string kMetaFunctionLeaderWord = "function"; + public const string kMetaFieldLeaderWord = "field"; + public const string kMetaEnumLeaderWord = "enum"; + const string kMetaNotSetWord = "not_set_internal"; + const string kMetaSeparate = "____"; + const string kMetaArgumentSeparate = "|"; + const string kMetaStartWord = kMetaLeaderWord + kMetaSeparate; + + public static bool IsRstudioAttribute(string meta) + { + return meta.StartsWith(kMetaStartWord); + } + + private static List DivideForMetaAttribute(string meta) + { + var attrArray = meta.Split(kMetaSeparate); + var retList = new List(); + + for(int i = 1; i < attrArray.Length; i++) + { + retList.Add(attrArray[i]); + } + + return retList; + } + + public static MetaAttribute ParseMetaStringFor(string meta, string needLeaderWord, out string errorMessage) + { + string feature = "", arguments = ""; + errorMessage = ""; + + if (!IsRstudioAttribute(meta)) + { + return null; + } + + List tmpList = DivideForMetaAttribute(meta); + if(tmpList.Count < 2 || tmpList[0] != needLeaderWord) + { + return null; + } + + var arrVal = tmpList[1].Split(kMetaArgumentSeparate); + feature = arrVal[0]; + if(arrVal.Length >= 2) + { + arguments = arrVal[1]; + } + + MetaAttribute attribute = new MetaAttribute(); + attribute.FeatureName = feature; + bool parseSuc = NamedParameterParser.ParseNamedParameters(arguments, attribute.ArgumentMap, out errorMessage); + if(parseSuc) + { + return attribute; + } + else + { + return null; + } + } + + public static MetaAttribute ParseMetaStringFor(string meta, out string errorMessage) + { + errorMessage = ""; + MetaAttribute attribute = new MetaAttribute(); + bool parseSuc = NamedParameterParser.ParseNamedParameters(meta, attribute.ArgumentMap, out errorMessage); + if(parseSuc) + { + return attribute; + } + + return null; + } + + } +} diff --git a/src/CppAst/attribute/NamedParamerterParser.cs b/src/CppAst/attribute/NamedParamerterParser.cs new file mode 100644 index 0000000..4fe0428 --- /dev/null +++ b/src/CppAst/attribute/NamedParamerterParser.cs @@ -0,0 +1,365 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Diagnostics; +using System.Resources; +using System.Threading.Tasks; +using System.Text; +using System.Threading; +using Irony.Parsing; + +namespace CppAst +{ + + public class NamedParameterParser + { + #region Embeded Types + public static class TerminalNames + { + public const string Identifier = "identifier", + Number = "number", + String = "string", + Boolean = "boolean", + Comma = ",", + Equal = "=", + Expression = "expression", + Assignment = "assignment", + LoopPair = "loop_pair", + NamedArguments = "named_arguments", + Args = "args", + Class = "class", + ClassName = "class_name", + NameSpace = "namespace", + Template = "template", + TemplateElem = "template_elem", + LeftBracket = "left_bracket", + RightBracket = "right_bracket"; + } + + [Language("NamedParameter.CppAst", "0.1", "Grammer for named parameter")] + public class NamedParameterGrammer : Irony.Parsing.Grammar + { + static NamedParameterGrammer sGrammer; + public static NamedParameterGrammer Instance + { + get + { + if (sGrammer == null) + { + sGrammer = new NamedParameterGrammer(); + } + return sGrammer; + } + } + + private NamedParameterGrammer() : + base(true) + { + #region Declare Terminals Here + NumberLiteral NUMBER = CreateNumberLiteral(TerminalNames.Number); + StringLiteral STRING_LITERAL = new StringLiteral(TerminalNames.String, "\"", StringOptions.AllowsAllEscapes); + IdentifierTerminal Name = new IdentifierTerminal(TerminalNames.Identifier); + + + // Regular Operators + var COMMA = ToTerm(TerminalNames.Comma); + var EQUAL = ToTerm(TerminalNames.Equal); + + #region Keywords + var TRUE_KEYWORD = Keyword("true"); + var FALSE_KEYWORD = Keyword("false"); + + + + var CLASS_KEYWORD = Keyword("__class"); + //var NEW = Keyword("new"); + #endregion + + #endregion + #region Declare NonTerminals Here + NonTerminal BOOLEAN = new NonTerminal(TerminalNames.Boolean); + NonTerminal EXPRESSION = new NonTerminal(TerminalNames.Expression); + NonTerminal ASSIGNMENT = new NonTerminal(TerminalNames.Assignment); + NonTerminal NAMED_ARGUMENTS = new NonTerminal(TerminalNames.NamedArguments); + NonTerminal LOOP_PAIR = new NonTerminal(TerminalNames.LoopPair); + NonTerminal ARGS = new NonTerminal(TerminalNames.Args); + NonTerminal CLASS_NAME = new NonTerminal(TerminalNames.ClassName); + NonTerminal NAMESPACE = new NonTerminal(TerminalNames.NameSpace); + NonTerminal TEMPLATE = new NonTerminal(TerminalNames.Template); + NonTerminal TEMPLATE_ELEM = new NonTerminal(TerminalNames.TemplateElem); + NonTerminal CLASS = new NonTerminal(TerminalNames.Class); + NonTerminal LEFT_BRACKET = new NonTerminal(TerminalNames.LeftBracket); + NonTerminal RIGHT_BRACKET = new NonTerminal(TerminalNames.RightBracket); + #endregion + + #region Place Rules Here + ////NORMAL_RECORD.Rule = Name + FIELD_FETCH; + + BOOLEAN.Rule = TRUE_KEYWORD | FALSE_KEYWORD; + LEFT_BRACKET.Rule = ToTerm("(") | ToTerm("{"); + RIGHT_BRACKET.Rule = ToTerm(")") | ToTerm("}"); + + NAMESPACE.Rule = MakePlusRule(NAMESPACE, ToTerm("::"), Name); + TEMPLATE_ELEM.Rule = MakeStarRule(ARGS, ToTerm(","), Name | Empty); + TEMPLATE.Rule = ToTerm("<") + TEMPLATE_ELEM + ToTerm(">"); + CLASS_NAME.Rule = NAMESPACE + TEMPLATE | Name + TEMPLATE | NAMESPACE | Name; + ARGS.Rule = MakeStarRule(ARGS, ToTerm(","), EXPRESSION | Empty); + CLASS.Rule = CLASS_KEYWORD + LEFT_BRACKET + CLASS_NAME + LEFT_BRACKET + ARGS + RIGHT_BRACKET + RIGHT_BRACKET; + + EXPRESSION.Rule = BOOLEAN | NUMBER | CLASS | STRING_LITERAL; + ASSIGNMENT.Rule = Name | Name + EQUAL + EXPRESSION; + LOOP_PAIR.Rule = MakeStarRule(COMMA + ASSIGNMENT); + NAMED_ARGUMENTS.Rule = ASSIGNMENT + LOOP_PAIR; + + this.Root = NAMED_ARGUMENTS; + #endregion + + #region Define Keywords and Register Symbols + ////this.RegisterBracePair("[", "]"); + + ////this.MarkPunctuation(",", ";"); + #endregion + } + + //Must create new overrides here in order to support the "Operator" token color + public new void RegisterOperators(int precedence, params string[] opSymbols) + { + RegisterOperators(precedence, Associativity.Left, opSymbols); + } + + //Must create new overrides here in order to support the "Operator" token color + public new void RegisterOperators(int precedence, Associativity associativity, params string[] opSymbols) + { + foreach (string op in opSymbols) + { + KeyTerm opSymbol = Operator(op); + opSymbol.Precedence = precedence; + opSymbol.Associativity = associativity; + } + } + + BnfExpression MakeStarRule(BnfTerm term) + { + return MakeStarRule(new NonTerminal(term.Name + "*"), term); + } + + public KeyTerm Keyword(string keyword) + { + var term = ToTerm(keyword); + // term.SetOption(TermOptions.IsKeyword, true); + // term.SetOption(TermOptions.IsReservedWord, true); + + this.MarkReservedWords(keyword); + term.EditorInfo = new TokenEditorInfo(TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None); + + return term; + } + + public KeyTerm Operator(string op) + { + string opCased = this.CaseSensitive ? op : op.ToLower(); + var term = new KeyTerm(opCased, op); + //term.SetOption(TermOptions.IsOperator, true); + + term.EditorInfo = new TokenEditorInfo(TokenType.Operator, TokenColor.Keyword, TokenTriggers.None); + + return term; + } + + protected static NumberLiteral CreateNumberLiteral(string name) + { + + NumberLiteral term = new NumberLiteral(name); + //default int types are Integer (32bit) -> LongInteger (BigInt); Try Int64 before BigInt: Better performance? + term.DefaultIntTypes = new TypeCode[] { TypeCode.Int32 }; + term.DefaultFloatType = TypeCode.Double; // it is default + ////term.AddPrefix("0x", NumberOptions.Hex); + + return term; + } + } + + #endregion + + + public static bool ParseNamedParameters(string content, Dictionary outNamedParameterDic, out string errorMessage) + { + errorMessage = ""; + + if(string.IsNullOrWhiteSpace(content)) + { + return true; + } + + Irony.Parsing.Parser parser = new Irony.Parsing.Parser(NamedParameterGrammer.Instance); + var ast = parser.Parse(content); + + if (!ast.HasErrors()) + { + ParseAssignment(ast.Root.ChildNodes[0], outNamedParameterDic); + + if(ast.Root.ChildNodes.Count >= 2 && ast.Root.ChildNodes[1].ChildNodes.Count > 0) + { + ParseLoopItem(ast.Root.ChildNodes[1].ChildNodes[0], outNamedParameterDic); + } + + return true; + } + else + { + errorMessage = ast.ParserMessages.ToString(); + } + + return false; + } + + + private static object ParseExpressionValue(ParseTreeNode node) + { + switch (node.Term.Name) + { + case TerminalNames.String: + return node.Token.ValueString; + case TerminalNames.Boolean: + if(node.ChildNodes[0].Term.Name == "false") + { + return false; + } + return true; + case TerminalNames.Number: + return node.Token.Value; + case TerminalNames.Template: + case TerminalNames.ClassName: + return ParseNodeChildren(node); + case TerminalNames.Class: + return ParseClassToken(node.ChildNodes); + case TerminalNames.Args: + return ParseClassArgs(node.ChildNodes); + case TerminalNames.NameSpace: + return ParseNodeListWithSeparator(node.ChildNodes, "::"); + case TerminalNames.TemplateElem: + return ParseNodeListWithSeparator(node.ChildNodes, ","); + case TerminalNames.LeftBracket: + case TerminalNames.RightBracket: + return node.ChildNodes[0].Token.Value; + default: + if (node.ChildNodes.Count == 0 && node.Token != null) + { + return node.Token.Value; + } + else if (node.ChildNodes.Count > 1) + { + throw new Exception("Can not run to here!"); + } + + return ParseExpressionValue(node.ChildNodes[0]); + } + } + + private static void ParseAssignment(ParseTreeNode node, Dictionary outNamedParameterDic) + { + string varName = node.ChildNodes[0].Token.ValueString; + if(!outNamedParameterDic.ContainsKey(varName)) + { + if (node.ChildNodes.Count == 1) + { + outNamedParameterDic.Add(varName, true); + } + else + { + outNamedParameterDic.Add(varName, ParseExpressionValue(node.ChildNodes[2].ChildNodes[0])); + } + } + } + + private static void ParseLoopItem(Irony.Parsing.ParseTreeNode loopNode, Dictionary outNamedParameterDic) + { + ParseAssignment(loopNode.ChildNodes[1], outNamedParameterDic); + + for (int i = 2; i < loopNode.ChildNodes.Count; i++) + { + ParseAssignment(loopNode.ChildNodes[i], outNamedParameterDic); + } + } + + private static string ParseNodeListWithSeparator(ParseTreeNodeList nodeList, string sep) + { + StringBuilder builder = new StringBuilder(); + foreach (var node in nodeList) + { + if (builder.Length > 0) + { + builder.Append(sep); + } + + builder.Append(ParseExpressionValue(node)); + } + + return builder.ToString(); + } + + private static string ParseNodeChildren(ParseTreeNode node) + { + StringBuilder builder = new StringBuilder(); + if (node.ChildNodes != null) + { + foreach (var child in node.ChildNodes) + { + builder.Append(ParseExpressionValue(child)); + } + } + + return builder.ToString(); + } + + private static string ParseClassArgs(ParseTreeNodeList nodeList) + { + StringBuilder builder = new StringBuilder(); + + foreach (var node in nodeList) + { + var nodeValue = ParseExpressionValue(node); + object realValue; + if (nodeValue is string) + { + realValue = "\"" + nodeValue + "\""; + } + else if (nodeValue is bool) + { + var str = nodeValue.ToString(); + realValue = str == "True" ? "true" : "false"; + } + else + { + realValue = nodeValue.ToString(); + } + + if (builder.Length > 0) + { + builder.Append(","); + } + + builder.Append(realValue); + } + + return builder.ToString(); + } + + private static StringBuilder ParseClassToken(ParseTreeNodeList nodeList) + { + if (nodeList.Count == 0) + { + return null; + } + + StringBuilder builder = new StringBuilder(); + for (int i = 2; i < nodeList.Count-1; i++) + { + builder.Append(ParseExpressionValue(nodeList[i])); + } + + return builder; + } + } +} From 49ed24f5de72b0a5b45df9bf4857f0c9e97eb5c7 Mon Sep 17 00:00:00 2001 From: zhouqili Date: Tue, 9 Apr 2024 10:53:20 +0800 Subject: [PATCH 2/2] add tests for the MetaAttribute --- .../AttributesTest/TestMetaAttribute.cs | 119 ++++++++++++++++++ .../CustomAttributeTool.cs | 0 .../NamedParamerterParser.cs | 0 3 files changed, 119 insertions(+) create mode 100644 src/CppAst.Tests/AttributesTest/TestMetaAttribute.cs rename src/CppAst/{attribute => AttributeUtils}/CustomAttributeTool.cs (100%) rename src/CppAst/{attribute => AttributeUtils}/NamedParamerterParser.cs (100%) diff --git a/src/CppAst.Tests/AttributesTest/TestMetaAttribute.cs b/src/CppAst.Tests/AttributesTest/TestMetaAttribute.cs new file mode 100644 index 0000000..a8cc604 --- /dev/null +++ b/src/CppAst.Tests/AttributesTest/TestMetaAttribute.cs @@ -0,0 +1,119 @@ +using System; + +namespace CppAst.Tests +{ + + public class TestMetaAttribute : InlineTestBase + { + [Test] + public void TestNamespaceMetaAttribute() + { + ParseAssert( @" + +#if !defined(__cppast) +#define __cppast(...) +#endif + +namespace __cppast(script, is_browsable=true, desc=""a namespace test"") TestNs{ + +} + +", compilation => + { + Assert.False(compilation.HasErrors); + + //annotate attribute support on namespace + var ns = compilation.Namespaces[0]; + + Assert.IsTrue(ns.MetaAttributes.QueryArgumentAsBool("script", false)); + Assert.IsFalse(!ns.MetaAttributes.QueryArgumentAsBool("is_browsable", false)); + Assert.AreEqual("a namespace test", ns.MetaAttributes.QueryArgumentAsString("desc", "")); + + } + ); + } + + [Test] + public void TestClassMetaAttribute() + { + + ParseAssert( @" + +#if !defined(__cppast) +#define __cppast(...) +#endif + +class __cppast(script, is_browsable=true, desc=""a class"") TestClass +{ + public: + __cppast(desc=""a member function"") + __cppast(desc2=""a member function 2"") + void TestMemberFunc(); + + __cppast(desc=""a member field"") + __cppast(desc2=""a member field 2"") + int X; +}; + +", compilation => + { + Assert.False(compilation.HasErrors); + + var cppClass = compilation.Classes[0]; + Assert.IsTrue(cppClass.MetaAttributes.QueryArgumentAsBool("script", false)); + Assert.IsFalse(!cppClass.MetaAttributes.QueryArgumentAsBool("is_browsable", false)); + Assert.AreEqual("a class", cppClass.MetaAttributes.QueryArgumentAsString("desc", "")); + + Assert.AreEqual(1, cppClass.Functions.Count); + Assert.AreEqual("a member function", cppClass.Functions[0].MetaAttributes.QueryArgumentAsString("desc", "")); + Assert.AreEqual("a member function 2", cppClass.Functions[0].MetaAttributes.QueryArgumentAsString("desc2", "")); + + Assert.AreEqual(1, cppClass.Fields.Count); + Assert.AreEqual("a member field", cppClass.Fields[0].MetaAttributes.QueryArgumentAsString("desc", "")); + Assert.AreEqual("a member field 2", cppClass.Fields[0].MetaAttributes.QueryArgumentAsString("desc2", "")); + + } + ); + } + + [Test] + public void TestTemplateMetaAttribute() + { + + ParseAssert( @" + +#if !defined(__cppast) +#define __cppast(...) +#endif + +template +class TestTemplateClass +{ + public: + + __cppast(desc=""a template member field"") + T X; +}; + +using IntClass __cppast(desc=""a template class for int"") = TestTemplateClass; +using DoubleClass __cppast(desc=""a template class for double"") = TestTemplateClass; + +typedef TestTemplateClass __cppast(desc=""a template class for float"") FloatClass; +", compilation => + { + Assert.False(compilation.HasErrors); + + var templateClass = compilation.Classes[0]; + Assert.AreEqual("a template member field", templateClass.Fields[0].MetaAttributes.QueryArgumentAsString("desc", "")); + + var intClass = compilation.Classes[1]; + var doubleClass = compilation.Classes[2]; + var floatClass = compilation.Classes[3]; + Assert.AreEqual("a template class for int", intClass.MetaAttributes.QueryArgumentAsString("desc", "")); + Assert.AreEqual("a template class for double", doubleClass.MetaAttributes.QueryArgumentAsString("desc", "")); + Assert.AreEqual("a template class for float", floatClass.MetaAttributes.QueryArgumentAsString("desc", "")); + } + ); + } + } +} \ No newline at end of file diff --git a/src/CppAst/attribute/CustomAttributeTool.cs b/src/CppAst/AttributeUtils/CustomAttributeTool.cs similarity index 100% rename from src/CppAst/attribute/CustomAttributeTool.cs rename to src/CppAst/AttributeUtils/CustomAttributeTool.cs diff --git a/src/CppAst/attribute/NamedParamerterParser.cs b/src/CppAst/AttributeUtils/NamedParamerterParser.cs similarity index 100% rename from src/CppAst/attribute/NamedParamerterParser.cs rename to src/CppAst/AttributeUtils/NamedParamerterParser.cs