Browse Source

Rewrite GenerateEnumFromMacros helper to also look into enum items.

instantiate-types-nested-templates
Joao Matos 5 years ago committed by João Matos
parent
commit
88fc5b9c36
  1. 12
      src/AST/Enumeration.cs
  2. 9
      src/Generator.Tests/Passes/TestPasses.cs
  3. 183
      src/Generator/Library.cs
  4. 11
      src/Generator/Utils/ExpressionEvaluator.cs
  5. 5
      tests/Native/Passes.h

12
src/AST/Enumeration.cs

@ -36,6 +36,18 @@ namespace CppSharp.AST
} }
} }
public Item()
{
}
public Item(Item item) : base(item)
{
Value = item.Value;
Expression = item.Expression;
ExplicitValue = item.ExplicitValue;
}
public override T Visit<T>(IDeclVisitor<T> visitor) public override T Visit<T>(IDeclVisitor<T> visitor)
{ {
return visitor.VisitEnumItemDecl(this); return visitor.VisitEnumItemDecl(this);

9
src/Generator.Tests/Passes/TestPasses.cs

@ -109,15 +109,16 @@ namespace CppSharp.Generator.Tests.Passes
Assert.IsNotNull(@enum); Assert.IsNotNull(@enum);
// Testing the values read for the enum // Testing the values read for the enum
Assert.AreEqual(0, @enum.Items[0].Value); // Decimal literal Assert.AreEqual(3, @enum.Items[0].Value); // Decimal literal
Assert.AreEqual(1, @enum.Items[1].Value); // Hex literal Assert.AreEqual(0, @enum.Items[1].Value); // Hex literal
Assert.AreEqual(2, @enum.Items[2].Value); // Hex literal with suffix Assert.AreEqual(1, @enum.Items[2].Value); // Hex literal with suffix
Assert.AreEqual(2, @enum.Items[3].Value); // Enum item
passBuilder.RemovePrefix("TEST_ENUM_ITEM_NAME_", RenameTargets.EnumItem); passBuilder.RemovePrefix("TEST_ENUM_ITEM_NAME_", RenameTargets.EnumItem);
passBuilder.AddPass(new CleanInvalidDeclNamesPass()); passBuilder.AddPass(new CleanInvalidDeclNamesPass());
passBuilder.RunPasses(pass => pass.VisitASTContext(AstContext)); passBuilder.RunPasses(pass => pass.VisitASTContext(AstContext));
Assert.That(@enum.Items[0].Name, Is.EqualTo("_0")); Assert.That(@enum.Items[1].Name, Is.EqualTo("_0"));
} }
[Test] [Test]

183
src/Generator/Library.cs

@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
using CodingSeb.ExpressionEvaluator; using CodingSeb.ExpressionEvaluator;
using CppSharp.AST; using CppSharp.AST;
using CppSharp.Generators; using CppSharp.Generators;
using CppSharp.Generators.CSharp;
using CppSharp.Passes; using CppSharp.Passes;
namespace CppSharp namespace CppSharp
@ -98,67 +99,105 @@ namespace CppSharp
{ {
Name = macro.Name, Name = macro.Name,
Expression = macro.Expression, Expression = macro.Expression,
Value = ParseMacroExpression(macro.Expression, @enum), Value = ParseEnumItemMacroExpression(macro, @enum),
Namespace = @enum Namespace = @enum
}; };
} }
static bool ParseToNumber(string num, Enumeration @enum, out long val) static bool EvaluateEnumExpression(string expr, Enumeration @enum, out object eval)
{ {
//ExpressionEvaluator does not work with hex var evaluator = new ExpressionEvaluator { Variables = new Dictionary<string, object>() };
if (num.StartsWith("0x", StringComparison.CurrentCultureIgnoreCase))
{
num = num.Substring(2);
// This is in case the literal contains suffix
num = Regex.Replace(num, "(?i)[ul]*$", String.Empty);
return long.TryParse(num, NumberStyles.HexNumber,
CultureInfo.CurrentCulture, out val);
}
ExpressionEvaluator evaluator = new ExpressionEvaluator(); // Include values of past items
//Include values of past items foreach (var item in @enum.Items)
evaluator.Variables = new Dictionary<string, object>();
foreach (Enumeration.Item item in @enum.Items)
{ {
//ExpressionEvaluator is requires lowercase variables evaluator.Variables.Add(item.Name, item.Value);
evaluator.Variables.Add(item.Name.ToLower(), item.Value);
} }
try try
{ {
var ret = evaluator.Evaluate("(long)" + num.ReplaceLineBreaks(" ").Replace('\\',' ')); expr = expr.ReplaceLineBreaks(" ").Replace('\\', ' ');
val = (long)ret; eval = evaluator.Evaluate(expr);
return true; return true;
} }
catch (ExpressionEvaluatorSyntaxErrorException) catch (Exception ex)
{ {
val = 0; throw new Exception($"Failed to evaluate expression: {ex.Message}");
return false;
} }
} }
static ulong ParseMacroExpression(string expression, Enumeration @enum) static ulong ParseEnumItemMacroExpression(MacroDefinition macro, Enumeration @enum)
{ {
var expression = macro.Expression;
// TODO: Handle string expressions // TODO: Handle string expressions
if (expression.Length == 3 && expression[0] == '\'' && expression[2] == '\'') // '0' || 'A' if (expression.Length == 3 && expression[0] == '\'' && expression[2] == '\'') // '0' || 'A'
return expression[1]; // return the ASCI code of this character return expression[1]; // return the ASCII code of this character
object eval;
try
{
EvaluateEnumExpression(expression, @enum, out eval);
}
catch (Exception)
{
// TODO: This should just throw, but we have a pre-existing behavior that expects malformed
// macro expressions to default to 0, see CSharp.h (MY_MACRO_TEST2_0), so do it for now.
return 0;
}
if (eval is ulong number)
return number;
long val; unchecked
return ParseToNumber(expression, @enum, out val) ? (ulong)val : 0; {
ulong val = ((ulong) (int) eval);
return val;
}
} }
public static Enumeration GenerateEnumFromMacros(this ASTContext context, string name, class CollectEnumItems : AstVisitor
params string[] macros)
{ {
var @enum = new Enumeration { Name = name }; public List<Enumeration.Item> Items;
private Regex regex;
var regexPattern = string.Join("|", macros.Select(pattern => $"({pattern}$)")); public CollectEnumItems(Regex regex)
var regex = new Regex(regexPattern); {
Items = new List<Enumeration.Item>();
this.regex = regex;
}
public override bool VisitEnumItemDecl(Enumeration.Item item)
{
if (AlreadyVisited(item))
return true;
// Prevents collecting previously generated items when generating enums from macros.
if (item.IsImplicit)
return true;
var match = regex.Match(item.Name);
if (match.Success)
Items.Add(item);
return true;
}
}
public static List<Enumeration.Item> FindMatchingEnumItems(this ASTContext context, Regex regex)
{
var collector = new CollectEnumItems(regex);
foreach (var unit in context.TranslationUnits)
collector.VisitTranslationUnit(unit);
return collector.Items;
}
public static List<MacroDefinition> FindMatchingMacros(this ASTContext context,
Regex regex, ref TranslationUnit unitToAttach)
{
int maxItems = 0; int maxItems = 0;
TranslationUnit unitToAttach = null; var macroDefinitions = new List<MacroDefinition>();
ulong maxValue = 0;
foreach (var unit in context.TranslationUnits) foreach (var unit in context.TranslationUnits)
{ {
@ -167,20 +206,16 @@ namespace CppSharp
foreach (var macro in unit.PreprocessedEntities.OfType<MacroDefinition>()) foreach (var macro in unit.PreprocessedEntities.OfType<MacroDefinition>())
{ {
var match = regex.Match(macro.Name); var match = regex.Match(macro.Name);
if (!match.Success) continue; if (!match.Success)
if (macro.Enumeration != null)
continue; continue;
// Skip this macro if the enum already has an item with same name. // if (macro.Enumeration != null)
if (@enum.Items.Exists(it => it.Name == macro.Name)) // continue;
continue;
var item = @enum.GenerateEnumItemFromMacro(macro); if (macroDefinitions.Exists(m => macro.Name == m.Name))
@enum.AddItem(item); continue;
macro.Enumeration = @enum;
maxValue = Math.Max(maxValue, item.Value); macroDefinitions.Add(macro);
numItems++; numItems++;
} }
@ -191,15 +226,57 @@ namespace CppSharp
} }
} }
if (@enum.Items.Count > 0) return macroDefinitions;
}
public static Enumeration GenerateEnumFromMacros(this ASTContext context, string name,
params string[] values)
{
TranslationUnit GetUnitFromItems(List<Enumeration.Item> list)
{
var units = list.Select(item => item.TranslationUnit)
.GroupBy(x => x)
.Select(y => new {Element = y.Key, Counter = y.Count()});
var translationUnit = units.OrderByDescending(u => u.Counter)
.FirstOrDefault()?.Element ?? null;
return translationUnit;
}
var @enum = new Enumeration { Name = name };
var regexPattern = string.Join("|", values.Select(pattern => $"({pattern}$)"));
var regex = new Regex(regexPattern);
var items = FindMatchingEnumItems(context, regex);
foreach (var item in items)
@enum.AddItem(item);
TranslationUnit macroUnit = null;
var macroDefs = FindMatchingMacros(context, regex, ref macroUnit);
foreach (var macro in macroDefs)
{ {
@enum.BuiltinType = new BuiltinType(GetUnderlyingTypeForEnumValue(maxValue)); // Skip this macro if the enum already has an item with same name.
@enum.Type = @enum.BuiltinType; if (@enum.Items.Exists(it => it.Name == macro.Name))
@enum.Namespace = unitToAttach; continue;
unitToAttach.Declarations.Add(@enum); var item = @enum.GenerateEnumItemFromMacro(macro);
@enum.AddItem(item);
macro.Enumeration = @enum;
} }
if (@enum.Items.Count <= 0)
return @enum;
var maxValue = @enum.Items.Max(i => i.Value);
@enum.BuiltinType = new BuiltinType(GetUnderlyingTypeForEnumValue(maxValue));
@enum.Type = @enum.BuiltinType;
var unit = macroUnit ?? GetUnitFromItems(items);
@enum.Namespace = unit;
unit.Declarations.Add(@enum);
return @enum; return @enum;
} }
@ -223,6 +300,12 @@ namespace CppSharp
if (maxValue < uint.MaxValue) if (maxValue < uint.MaxValue)
return PrimitiveType.UInt; return PrimitiveType.UInt;
if (maxValue < long.MaxValue)
return PrimitiveType.Long;
if (maxValue < ulong.MaxValue)
return PrimitiveType.ULong;
throw new NotImplementedException(); throw new NotImplementedException();
} }

11
src/Generator/Utils/ExpressionEvaluator.cs

@ -1577,6 +1577,17 @@ namespace CodingSeb.ExpressionEvaluator
protected virtual bool EvaluateNumber(string expression, Stack<object> stack, ref int i) protected virtual bool EvaluateNumber(string expression, Stack<object> stack, ref int i)
{ {
string restOfExpression = expression.Substring(i); string restOfExpression = expression.Substring(i);
// CPPSHARP
if (restOfExpression.StartsWith("0x", StringComparison.CurrentCultureIgnoreCase))
{
// This is in case the literal contains suffix
var cleanedUp = Regex.Replace(restOfExpression, "(?i)[ul]*$", string.Empty);
i += restOfExpression.Length - cleanedUp.Length;
restOfExpression = cleanedUp;
}
//
Match numberMatch = Regex.Match(restOfExpression, numberRegexPattern, RegexOptions.IgnoreCase); Match numberMatch = Regex.Match(restOfExpression, numberRegexPattern, RegexOptions.IgnoreCase);
Match otherBaseMatch = otherBasesNumberRegex.Match(restOfExpression); Match otherBaseMatch = otherBasesNumberRegex.Match(restOfExpression);

5
tests/Native/Passes.h

@ -42,6 +42,11 @@ struct TestReadOnlyProperties
#define TEST_ENUM_ITEM_NAME_1 0x1 #define TEST_ENUM_ITEM_NAME_1 0x1
#define TEST_ENUM_ITEM_NAME_2 0x2U #define TEST_ENUM_ITEM_NAME_2 0x2U
enum
{
TEST_ENUM_ITEM_NAME_3 = 3
};
// TestStructInheritance // TestStructInheritance
struct S1 { int F1, F2; }; struct S1 { int F1, F2; };
struct S2 : S1 { int F3; }; struct S2 : S1 { int F3; };

Loading…
Cancel
Save