Skip to content

Commit

Permalink
Merge pull request #98 from daliziql/feature/meta-attribute
Browse files Browse the repository at this point in the history
Add MetaAttributes to support the parsing of AnnotateAttribute for namespaces, classes, functions, fields, etc.
  • Loading branch information
xoofx authored Apr 9, 2024
2 parents bb0f83a + 49ed24f commit 3eb5963
Show file tree
Hide file tree
Showing 14 changed files with 814 additions and 16 deletions.
119 changes: 119 additions & 0 deletions src/CppAst.Tests/AttributesTest/TestMetaAttribute.cs
Original file line number Diff line number Diff line change
@@ -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 <typename T>
class TestTemplateClass
{
public:
__cppast(desc=""a template member field"")
T X;
};
using IntClass __cppast(desc=""a template class for int"") = TestTemplateClass<int>;
using DoubleClass __cppast(desc=""a template class for double"") = TestTemplateClass<double>;
typedef TestTemplateClass<float> __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", ""));
}
);
}
}
}
208 changes: 208 additions & 0 deletions src/CppAst/AttributeUtils/CustomAttributeTool.cs
Original file line number Diff line number Diff line change
@@ -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<string, object> ArgumentMap = new Dictionary<string, object>();

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<string> 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<MetaAttribute> MetaList { get; private set; } = new List<MetaAttribute>();

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<string> DivideForMetaAttribute(string meta)
{
var attrArray = meta.Split(kMetaSeparate);
var retList = new List<string>();

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<string> 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;
}

}
}
Loading

0 comments on commit 3eb5963

Please sign in to comment.