Regexp based Member renaming (#66)
Closes #28
- Added Member renaming using `member-rename` subkey in declarations.
- Renames struct/enum members and function parameter names using regexp/full matching.
- Added/Updated renaming tests.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ba4e384..21853fb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
- Updated header config. Header `entry-points` and `include-directives` are now specified under `headers` key. Glob syntax is allowed.
- Updated declaration `include`/`exclude` config. These are now specified as a list.
- Added Regexp based declaration renaming using `rename` subkey.
+- Added Regexp based member renaming for structs, enums and functions using `member-rename` subkey. `prefix` and `prefix-replacement` subkeys have been removed.
# 0.1.5
- Added support for parsing macros and anonymous unnamed enums. These are generated as top level constants.
diff --git a/README.md b/README.md
index ed8ff82..477a0e9 100644
--- a/README.md
+++ b/README.md
@@ -112,11 +112,17 @@
- [a-z][a-zA-Z0-9]* # Matches using regexp.
- prefix.* # '.' matches any character.
- someFuncName # Matches with exact name
- - anotherName # full names have higher priority.
+ - anotherName # Full names have higher priority.
rename:
'clang_(.*)': '$1' # Regexp groups based replacement.
'clang_dispose': 'dispose' # full name matches have higher priority.
'_(.*)': '$1' # Removes '_' from beginning of a name.
+enums:
+ member-rename:
+ '(.*)': # Matches any enum.
+ '_(.*)': '$1' # Removes '_' from beginning enum member name.
+ 'CXTypeKind': # Full names have higher priority.
+ 'CXType(.*)': '$1' # $1 keeps only the 1st group i.e '(.*)'.
</code></pre></td>
</tr>
<tr>
diff --git a/lib/src/code_generator/enum_class.dart b/lib/src/code_generator/enum_class.dart
index 5a96c39..1a383ce 100644
--- a/lib/src/code_generator/enum_class.dart
+++ b/lib/src/code_generator/enum_class.dart
@@ -72,8 +72,14 @@
/// Represents a single value in an enum.
class EnumConstant {
+ final String originalName;
final String dartDoc;
final String name;
final int value;
- const EnumConstant({@required this.name, @required this.value, this.dartDoc});
+ const EnumConstant({
+ String originalName,
+ @required this.name,
+ @required this.value,
+ this.dartDoc,
+ }) : originalName = originalName ?? name;
}
diff --git a/lib/src/code_generator/func.dart b/lib/src/code_generator/func.dart
index 62194c1..0e24fb1 100644
--- a/lib/src/code_generator/func.dart
+++ b/lib/src/code_generator/func.dart
@@ -147,8 +147,10 @@
/// Represents a Function's parameter.
class Parameter {
+ final String originalName;
String name;
final Type type;
- Parameter({this.name, @required this.type});
+ Parameter({String originalName, this.name, @required this.type})
+ : originalName = originalName ?? name;
}
diff --git a/lib/src/code_generator/struc.dart b/lib/src/code_generator/struc.dart
index dec4c2c..24e6727 100644
--- a/lib/src/code_generator/struc.dart
+++ b/lib/src/code_generator/struc.dart
@@ -151,10 +151,16 @@
class Member {
final String dartDoc;
+ final String originalName;
final String name;
final Type type;
- const Member({@required this.name, @required this.type, this.dartDoc});
+ const Member({
+ String originalName,
+ @required this.name,
+ @required this.type,
+ this.dartDoc,
+ }) : originalName = originalName ?? name;
}
// Helper bindings for struct array.
diff --git a/lib/src/config_provider/config_types.dart b/lib/src/config_provider/config_types.dart
index 302e69a..b699239 100644
--- a/lib/src/config_provider/config_types.dart
+++ b/lib/src/config_provider/config_types.dart
@@ -95,15 +95,22 @@
class Declaration {
final Includer _includer;
final Renamer _renamer;
+ final MemberRenamer _memberRenamer;
Declaration({
Includer includer,
Renamer renamer,
+ MemberRenamer memberRenamer,
}) : _includer = includer ?? Includer(),
- _renamer = renamer ?? Renamer();
+ _renamer = renamer ?? Renamer(),
+ _memberRenamer = memberRenamer ?? MemberRenamer();
/// Applies renaming and returns the result.
- String renameUsingConfig(String name) => _renamer.renameUsingConfig(name);
+ String renameUsingConfig(String name) => _renamer.rename(name);
+
+ /// Applies member renaming and returns the result.
+ String renameMemberUsingConfig(String declaration, String member) =>
+ _memberRenamer.rename(declaration, member);
/// Checks if a name is allowed by a filter.
bool shouldInclude(String name) => _includer.shouldInclude(name);
@@ -112,23 +119,32 @@
/// Matches `$<single_digit_int>`, value can be accessed in group 1 of match.
final replaceGroupRegexp = RegExp(r'\$([0-9])');
-class RenamePattern {
+/// Match/rename using [regExp].
+class RegExpRenamer {
final RegExp regExp;
final String replacementPattern;
- RenamePattern(this.regExp, this.replacementPattern);
+ RegExpRenamer(this.regExp, this.replacementPattern);
/// Returns true if [str] has a full match with [regExp].
bool matches(String str) => quiver.matchesFull(regExp, str);
/// Renames [str] according to [replacementPattern].
+ ///
+ /// Returns [str] if [regExp] doesn't have a full match.
String rename(String str) {
- if (quiver.matchesFull(regExp, str)) {
+ if (matches(str)) {
+ // Get match.
final regExpMatch = regExp.firstMatch(str);
+
+ /// Get group values.
+ /// E.g for `str`: `clang_dispose` and `regExp`: `clang_(.*)`
+ /// groups will be `0`: `clang_disponse`, `1`: `dispose`.
final groups = regExpMatch.groups(
List.generate(regExpMatch.groupCount, (index) => index) +
[regExpMatch.groupCount]);
+ /// Replace all `$<int>` symbols with respective groups (if any).
final result =
replacementPattern.replaceAllMapped(replaceGroupRegexp, (match) {
final groupInt = int.parse(match.group(1));
@@ -136,7 +152,6 @@
});
return result;
} else {
- /// We return [str] if pattern doesn't have a full match.
return str;
}
}
@@ -147,8 +162,8 @@
}
}
+/// Handles `include/exclude` logic for a declaration.
class Includer {
- // matchers
final List<RegExp> _includeMatchers;
final Set<String> _includeFull;
final List<RegExp> _excludeMatchers;
@@ -164,6 +179,9 @@
_excludeMatchers = excludeMatchers ?? [],
_excludeFull = excludeFull ?? {};
+ /// Returns true if [name] is allowed.
+ ///
+ /// Exclude overrides include.
bool shouldInclude(String name) {
if (_excludeFull.contains(name)) {
return false;
@@ -195,17 +213,22 @@
}
}
+/// Handles `full/regexp` renaming logic.
class Renamer {
final Map<String, String> _renameFull;
- final List<RenamePattern> _renameMatchers;
+ final List<RegExpRenamer> _renameMatchers;
Renamer({
- List<RenamePattern> renamePatterns,
+ List<RegExpRenamer> renamePatterns,
Map<String, String> renameFull,
}) : _renameMatchers = renamePatterns ?? [],
_renameFull = renameFull ?? {};
- String renameUsingConfig(String name) {
+ Renamer.noRename()
+ : _renameMatchers = [],
+ _renameFull = {};
+
+ String rename(String name) {
// Apply full rename (if any).
if (_renameFull.containsKey(name)) {
return _renameFull[name];
@@ -222,3 +245,59 @@
return name;
}
}
+
+/// Match declaration name using [declarationRegExp].
+class RegExpMemberRenamer {
+ final RegExp declarationRegExp;
+ final Renamer memberRenamer;
+
+ RegExpMemberRenamer(this.declarationRegExp, this.memberRenamer);
+
+ /// Returns true if [declaration] has a full match with [regExp].
+ bool matchesDeclarationName(String declaration) =>
+ quiver.matchesFull(declarationRegExp, declaration);
+
+ @override
+ String toString() {
+ return 'DeclarationRegExp: $declarationRegExp, MemberRenamer: $memberRenamer';
+ }
+}
+
+/// Handles `full/regexp` member renaming.
+class MemberRenamer {
+ final Map<String, Renamer> _memberRenameFull;
+ final List<RegExpMemberRenamer> _memberRenameMatchers;
+
+ final Map<String, Renamer> _cache = {};
+
+ MemberRenamer({
+ Map<String, Renamer> memberRenameFull,
+ List<RegExpMemberRenamer> memberRenamePattern,
+ }) : _memberRenameFull = memberRenameFull ?? {},
+ _memberRenameMatchers = memberRenamePattern ?? [];
+
+ String rename(String declaration, String member) {
+ if (_cache.containsKey(declaration)) {
+ return _cache[declaration].rename(member);
+ }
+
+ // Apply full rename (if any).
+ if (_memberRenameFull.containsKey(declaration)) {
+ // Add to cache.
+ _cache[declaration] = _memberRenameFull[declaration];
+ return _cache[declaration].rename(member);
+ }
+
+ // Apply rename regexp (if matches).
+ for (final renamer in _memberRenameMatchers) {
+ if (renamer.matchesDeclarationName(declaration)) {
+ // Add to cache.
+ _cache[declaration] = renamer.memberRenamer;
+ return _cache[declaration].rename(member);
+ }
+ }
+
+ // No renaming is provided for this declaration, return unchanged.
+ return member;
+ }
+}
diff --git a/lib/src/config_provider/spec_utils.dart b/lib/src/config_provider/spec_utils.dart
index 8b29d9d..ddf6b89 100644
--- a/lib/src/config_provider/spec_utils.dart
+++ b/lib/src/config_provider/spec_utils.dart
@@ -182,8 +182,10 @@
includeFull = <String>{},
excludeMatchers = <RegExp>[],
excludeFull = <String>{};
- final renamePatterns = <RenamePattern>[];
+ final renamePatterns = <RegExpRenamer>[];
final renameFull = <String, String>{};
+ final memberRenamePatterns = <RegExpMemberRenamer>[];
+ final memberRenamerFull = <String, Renamer>{};
final include = (yamlMap[strings.include] as YamlList)?.cast<String>();
if (include != null) {
@@ -215,7 +217,43 @@
renameFull[str] = rename[str];
} else {
renamePatterns
- .add(RenamePattern(RegExp(str, dotAll: true), rename[str]));
+ .add(RegExpRenamer(RegExp(str, dotAll: true), rename[str]));
+ }
+ }
+ }
+
+ final memberRename =
+ (yamlMap[strings.memberRename] as YamlMap)?.cast<String, YamlMap>();
+
+ if (memberRename != null) {
+ for (final decl in memberRename.keys) {
+ final renamePatterns = <RegExpRenamer>[];
+ final renameFull = <String, String>{};
+
+ final memberRenameMap = memberRename[decl].cast<String, String>();
+ for (final member in memberRenameMap.keys) {
+ if (isFullDeclarationName(member)) {
+ renameFull[member] = memberRenameMap[member];
+ } else {
+ renamePatterns.add(RegExpRenamer(
+ RegExp(member, dotAll: true), memberRenameMap[member]));
+ }
+ }
+ if (isFullDeclarationName(decl)) {
+ memberRenamerFull[decl] = Renamer(
+ renameFull: renameFull,
+ renamePatterns: renamePatterns,
+ );
+ } else {
+ memberRenamePatterns.add(
+ RegExpMemberRenamer(
+ RegExp(decl, dotAll: true),
+ Renamer(
+ renameFull: renameFull,
+ renamePatterns: renamePatterns,
+ ),
+ ),
+ );
}
}
}
@@ -231,6 +269,10 @@
renameFull: renameFull,
renamePatterns: renamePatterns,
),
+ memberRenamer: MemberRenamer(
+ memberRenameFull: memberRenamerFull,
+ memberRenamePattern: memberRenamePatterns,
+ ),
);
}
@@ -253,6 +295,29 @@
}
}
}
+ } else if (key == strings.memberRename) {
+ if (!checkType<YamlMap>([name, key as String], value[key])) {
+ _result = false;
+ } else {
+ for (final declNameKey in value[key].keys) {
+ if (!checkType<YamlMap>(
+ [name, key as String, declNameKey as String],
+ value[key][declNameKey])) {
+ _result = false;
+ } else {
+ for (final memberNameKey in value[key][declNameKey].keys) {
+ if (!checkType<String>([
+ name,
+ key as String,
+ declNameKey as String,
+ memberNameKey as String,
+ ], value[key][declNameKey][memberNameKey])) {
+ _result = false;
+ }
+ }
+ }
+ }
+ }
} else {
_logger.severe("Unknown key '$key' in '$name'.");
_result = false;
diff --git a/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
index 99b6b57..0fbb9e8 100644
--- a/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
@@ -100,7 +100,11 @@
cursor,
nesting.length + commentPrefix.length,
),
- name: cursor.spelling(),
+ originalName: cursor.spelling(),
+ name: config.enumClassDecl.renameMemberUsingConfig(
+ _stack.top.enumClass.originalName,
+ cursor.spelling(),
+ ),
value: clang.clang_getEnumConstantDeclValue_wrap(cursor)),
);
}
diff --git a/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
index f3e1507..d958c57 100644
--- a/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
@@ -36,7 +36,7 @@
_logger.fine('++++ Adding Function: ${cursor.completeStringRepr()}');
final rt = _getFunctionReturnType(cursor);
- final parameters = _getParameters(cursor);
+ final parameters = _getParameters(cursor, funcName);
//TODO(3): Remove this when support for Structs by value arrives.
if (rt.broadType == BroadType.Struct || _stack.top.structByValueParameter) {
@@ -80,7 +80,8 @@
return cursor.returnType().toCodeGenTypeAndDispose();
}
-List<Parameter> _getParameters(Pointer<clang_types.CXCursor> cursor) {
+List<Parameter> _getParameters(
+ Pointer<clang_types.CXCursor> cursor, String funcName) {
final parameters = <Parameter>[];
final totalArgs = clang.clang_Cursor_getNumArguments_wrap(cursor);
@@ -102,7 +103,8 @@
/// If [pn] is null or empty, its set to `arg$i` by code_generator.
parameters.add(
Parameter(
- name: pn,
+ originalName: pn,
+ name: config.functionDecl.renameMemberUsingConfig(funcName, pn),
type: pt,
),
);
diff --git a/lib/src/header_parser/sub_parsers/structdecl_parser.dart b/lib/src/header_parser/sub_parsers/structdecl_parser.dart
index 32e3a76..5081aed 100644
--- a/lib/src/header_parser/sub_parsers/structdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/structdecl_parser.dart
@@ -130,7 +130,11 @@
cursor,
nesting.length + commentPrefix.length,
),
- name: cursor.spelling(),
+ originalName: cursor.spelling(),
+ name: config.structDecl.renameMemberUsingConfig(
+ _stack.top.struc.originalName,
+ cursor.spelling(),
+ ),
type: mt,
),
);
diff --git a/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart b/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
index 8f8487b..fedeb7e 100644
--- a/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
@@ -60,7 +60,11 @@
void _addUnNamedEnumConstant(Pointer<clang_types.CXCursor> cursor) {
_constants.add(
Constant(
- name: cursor.spelling(),
+ originalName: cursor.spelling(),
+ name: config.enumClassDecl.renameMemberUsingConfig(
+ '', // Un-named enum constants have an empty declaration name.
+ cursor.spelling(),
+ ),
rawType: 'int',
rawValue: clang.clang_getEnumConstantDeclValue_wrap(cursor).toString(),
),
diff --git a/lib/src/strings.dart b/lib/src/strings.dart
index b432442..e055347 100644
--- a/lib/src/strings.dart
+++ b/lib/src/strings.dart
@@ -46,6 +46,7 @@
const include = 'include';
const exclude = 'exclude';
const rename = 'rename';
+const memberRename = 'member-rename';
const sizemap = 'size-map';
// Sizemap values.
diff --git a/test/rename_tests/rename.h b/test/rename_tests/rename.h
index 8ef04e8..8ff6d88 100644
--- a/test/rename_tests/rename.h
+++ b/test/rename_tests/rename.h
@@ -15,10 +15,21 @@
struct FullMatchStruct3
{
};
+struct MemberRenameStruct4
+{
+ int _underscore;
+ float fullMatch;
+};
+
+struct AnyMatchStruct5
+{
+ int _underscore;
+};
void func1(struct Struct1 *s);
void test_func2(struct Test_Struct2 *s);
void fullMatch_func3(struct FullMatchStruct3 *s);
+void memberRename_func4(int _underscore, float fullMatch, int);
enum Enum1
{
@@ -38,3 +49,13 @@
j = 1,
k = 2
};
+enum MemberRenameEnum4
+{
+ _underscore = 0,
+ fullMatch = 1
+};
+enum
+{
+ _unnamed_underscore = 0,
+ unnamedFullMatch = 1
+};
diff --git a/test/rename_tests/rename_test.dart b/test/rename_tests/rename_test.dart
index 3461cb6..c3425cc 100644
--- a/test/rename_tests/rename_test.dart
+++ b/test/rename_tests/rename_test.dart
@@ -36,19 +36,36 @@
'test_(.*)': '\$1'
'.*': '$functionPrefix\$0'
'fullMatch_func3': 'func3'
-
+ ${strings.memberRename}:
+ 'memberRename_.*':
+ '_(.*)': '\$1'
+ 'fullMatch': 'fullMatchSuccess'
+ '': 'unnamed'
structs:
${strings.rename}:
'Test_(.*)': '\$1'
'.*': '$structPrefix\$0'
'FullMatchStruct3': 'Struct3'
+ ${strings.memberRename}:
+ 'MemberRenameStruct4':
+ '_(.*)': '\$1'
+ 'fullMatch': 'fullMatchSuccess'
+ '.*':
+ '_(.*)': '\$1'
enums:
${strings.rename}:
'Test_(.*)': '\$1'
'.*': '$enumPrefix\$0'
'FullMatchEnum3': 'Enum3'
+ ${strings.memberRename}:
+ 'MemberRenameEnum4':
+ '_(.*)': '\$1'
+ 'fullMatch': 'fullMatchSuccess'
+ '':
+ '_(.*)': '\$1'
+ 'unnamedFullMatch': 'unnamedFullMatchSuccess'
macros:
${strings.rename}:
@@ -107,6 +124,30 @@
expect(actual.getBindingAsString('Macro3'),
expected.getBindingAsString('Macro3'));
});
+ test('Struct member rename', () {
+ expect(actual.getBindingAsString('${structPrefix}MemberRenameStruct4'),
+ expected.getBindingAsString('${structPrefix}MemberRenameStruct4'));
+ });
+ test('Any Struct member rename', () {
+ expect(actual.getBindingAsString('${structPrefix}AnyMatchStruct5'),
+ expected.getBindingAsString('${structPrefix}AnyMatchStruct5'));
+ });
+ test('Function member rename', () {
+ expect(actual.getBindingAsString('${functionPrefix}memberRename_func4'),
+ expected.getBindingAsString('${functionPrefix}memberRename_func4'));
+ });
+ test('Enum member rename', () {
+ expect(actual.getBindingAsString('${enumPrefix}MemberRenameEnum4'),
+ expected.getBindingAsString('${enumPrefix}MemberRenameEnum4'));
+ });
+ test('unnamed Enum regexp rename', () {
+ expect(actual.getBindingAsString('unnamed_underscore'),
+ expected.getBindingAsString('unnamed_underscore'));
+ });
+ test('unnamed Enum full match rename', () {
+ expect(actual.getBindingAsString('unnamedFullMatchSuccess'),
+ expected.getBindingAsString('unnamedFullMatchSuccess'));
+ });
});
}
@@ -156,9 +197,52 @@
),
],
),
+ Func(
+ name: '${functionPrefix}memberRename_func4',
+ originalName: 'memberRename_func4',
+ returnType: Type.nativeType(
+ SupportedNativeType.Void,
+ ),
+ parameters: [
+ Parameter(
+ name: 'underscore',
+ type: Type.nativeType(SupportedNativeType.Int32),
+ ),
+ Parameter(
+ name: 'fullMatchSuccess',
+ type: Type.nativeType(SupportedNativeType.Float),
+ ),
+ Parameter(
+ name: 'unnamed',
+ type: Type.nativeType(SupportedNativeType.Int32),
+ ),
+ ],
+ ),
struc1,
struc2,
struc3,
+ Struc(
+ name: '${structPrefix}MemberRenameStruct4',
+ members: [
+ Member(
+ name: 'underscore',
+ type: Type.nativeType(SupportedNativeType.Int32),
+ ),
+ Member(
+ name: 'fullMatchSuccess',
+ type: Type.nativeType(SupportedNativeType.Float),
+ ),
+ ],
+ ),
+ Struc(
+ name: '${structPrefix}AnyMatchStruct5',
+ members: [
+ Member(
+ name: 'underscore',
+ type: Type.nativeType(SupportedNativeType.Int32),
+ ),
+ ],
+ ),
EnumClass(
name: '${enumPrefix}Enum1',
enumConstants: [
@@ -183,6 +267,13 @@
EnumConstant(name: 'k', value: 2),
],
),
+ EnumClass(
+ name: '${enumPrefix}MemberRenameEnum4',
+ enumConstants: [
+ EnumConstant(name: 'underscore', value: 0),
+ EnumConstant(name: 'fullMatchSuccess', value: 1),
+ ],
+ ),
Constant(
name: '${macroPrefix}Macro1',
rawType: 'int',
@@ -198,6 +289,16 @@
rawType: 'int',
rawValue: '3',
),
+ Constant(
+ name: 'unnamed_underscore',
+ rawType: 'int',
+ rawValue: '0',
+ ),
+ Constant(
+ name: 'unnamedFullMatchSuccess',
+ rawType: 'int',
+ rawValue: '1',
+ ),
],
);
}