Skip to content

Commit

Permalink
* Improve Parser capabilities for operator and function template…
Browse files Browse the repository at this point in the history
…s (pull #732)
  • Loading branch information
HGuillemet authored Jan 1, 2024
1 parent 9855bf6 commit a15c408
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 89 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

* Improve `Parser` capabilities for `operator` and function templates ([pull #732](https://github.com/bytedeco/javacpp/pull/732))
* Fix `Parser` failing on nested initializer lists and on attributes found inside `enum` declarations
* Fix `Parser` for basic containers like `std::optional<std::pair<int,int> >` ([issue #718](https://github.com/bytedeco/javacpp/issues/718))
* Add support for `std::basic_string` basic container ([issue bytedeco/javacpp-presets#1311](https://github.com/bytedeco/javacpp-presets/issues/1311))
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/org/bytedeco/javacpp/tools/DeclarationList.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,21 @@ public boolean add(Declaration decl, String fullName) {
boolean add = true;
if (templateMap != null && templateMap.empty() && !decl.custom && (decl.type != null || decl.declarator != null)) {
// method templates cannot be declared in Java, but make sure to make their
// info available on request (when Info.javaNames is set) to be able to create instances
// info available on request (when Info.javaNames or Info.define is set) to be able to create instances
if (infoIterator == null) {
Type type = templateMap.type = decl.type;
Declarator dcl = templateMap.declarator = decl.declarator;
for (String name : new String[] {fullName, dcl != null ? dcl.cppName : type.cppName}) {
for (String name : new String[] {fullName, dcl != null ? (dcl.type.constructor ? Parser.constructorName(dcl.cppName) : dcl.cppName) : type.cppName}) {
if (name == null) {
continue;
}
List<Info> infoList = infoMap.get(name);
boolean hasJavaName = false;
boolean hasJavaNameOrDefine = false;
for (Info info : infoList) {
hasJavaName |= info.javaNames != null && info.javaNames.length > 0;
hasJavaNameOrDefine |= info.javaNames != null && info.javaNames.length > 0 || info.define;
}
if (!decl.function || hasJavaName) {
infoIterator = infoList.size() > 0 ? infoList.listIterator() : null;
if (!decl.function || hasJavaNameOrDefine) {
infoIterator = infoList.size() > 0 ? new ArrayList<>(infoList).listIterator() : null;
break;
}
}
Expand Down
53 changes: 15 additions & 38 deletions src/main/java/org/bytedeco/javacpp/tools/InfoMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ public class InfoMap extends HashMap<String,List<Info>> {
"std::function", "std::basic_string"))
.put(new Info("basic/types").cppTypes("signed", "unsigned", "char", "short", "int", "long", "bool", "float", "double",
"__int8", "__int16", "__int32", "__int64", "_Bool", "_Complex", "_Imaginary", "complex", "imaginary"))
.put(new Info("deprecated").annotations("@Deprecated"))
.put(new Info("noexcept").annotations("@NoException(true)"))

.put(new Info("__COUNTER__").cppText("#define __COUNTER__ 0"))
Expand Down Expand Up @@ -185,6 +184,21 @@ String normalize(String name, boolean unconst, boolean untemplate) {
if (name == null || name.length() == 0 || name.startsWith("basic/")) {
return name;
}
if (untemplate) {
// Remove template arguments in the last NS component only, and not in parameters, if any
List<String> comps = Templates.splitNamespace(name, true);
int paramsIdx = comps.size() - 1;
String lastComp = comps.get(paramsIdx - 1);
comps.set(paramsIdx - 1, Templates.strip(lastComp));
name = comps.get(0);
for (int i = 1; i < paramsIdx; i++) {
name += "::" + comps.get(i);
}
name += comps.get(paramsIdx);
if (name.isEmpty()) {
return name;
}
}
boolean foundConst = false, simpleType = true;
String prefix = null;
Token[] tokens = new Tokenizer(name, null, 0).tokenize();
Expand Down Expand Up @@ -216,43 +230,6 @@ String normalize(String name, boolean unconst, boolean untemplate) {
for (int i = 1; i < n; i++) {
name += " " + tokens[i].value;
}
} else if (untemplate) {
int count = 0, lastColon = -1, template = -1, parameters = n;
for (int i = 0; i < n; i++) {
if (tokens[i].match('<')) {
count++;
} else if (tokens[i].match('>')) {
count--;
}
if (count == 0 && tokens[i].match("::")) {
lastColon = i;
} else if (count == 0 && tokens[i].match('(')) {
parameters = i;
break;
}
}
for (int i = lastColon + 1; i < parameters; i++) {
if (tokens[i].match('<')) {
if (count == 0) {
template = i;
}
count++;
} else if (tokens[i].match('>')) {
count--;
if (count == 0 && i + 1 != parameters) {
template = -1;
}
}
}
if (template >= 0) {
name = foundConst ? "const " : "";
for (int i = 0; i < template; i++) {
name += tokens[i];
}
for (int i = parameters; i < n; i++) {
name += tokens[i].spacing + tokens[i];
}
}
}
if (unconst && foundConst) {
name = name.substring(name.indexOf("const") + 5);
Expand Down
134 changes: 92 additions & 42 deletions src/main/java/org/bytedeco/javacpp/tools/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,17 @@ static String upcastMethodName(String javaName) {
return "as" + Character.toUpperCase(shortName.charAt(0)) + shortName.substring(1);
}

/** Returns the name of the constructor of class cppName, to be used as keys in infoMap */
/**
* Constructors have 2 kinds of fully qualified name:
* the calling name of a constructor, used when calling the constructor, e.g.:
* NS::CN(int)
* and the declaration name, used when defining the constructor outside its class or when referencing a constructor
* with using to inherit constructors of base class.
* NS::CN::CN(int)
* Declarator.cppName contains the calling name, and this method returns the declaration name.
* Keys in info map should use the declaration name, because the calling name cannot specify
* arguments in case of constructor templates, and to avoid confusion between classes and constructores info.
*/
static String constructorName(String cppName) {
String constructorName = Templates.strip(cppName);
int namespace = constructorName.lastIndexOf("::");
Expand Down Expand Up @@ -752,6 +762,28 @@ Type[] templateArguments(Context context) throws ParserException {
return arguments.toArray(new Type[0]);
}

/**
* Read and return the operator following an operator keyword:
* any of new, delete, + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= <=>(since C++20) && || ++ -- , ->* -> ( ) [ ]
* taking care of template arguments, if any.
*/
private String operator(Context context) throws ParserException {
String res = tokens.get().toString(); // Can be '('
int lenFirstToken = res.length();
String s = "";
tokens.next();
int backIndex = tokens.index;
for (Token token = tokens.get(); !token.match('(', ';', Token.EOF); token = tokens.next()) {
s += token;
}
s = Templates.strip(s);
tokens.index = backIndex;
for (Token token = tokens.get(); s.length() > res.length() - lenFirstToken; token = tokens.next()) {
res += token;
}
return res;
}

Type type(Context context) throws ParserException {
return type(context, false);
}
Expand Down Expand Up @@ -865,7 +897,8 @@ Type type(Context context, boolean definition) throws ParserException {
} else if (type.cppName.endsWith("::")) {
type.operator = true;
tokens.next();
break;
type.cppName += operator(context);
continue;
} else {
break;
}
Expand Down Expand Up @@ -957,18 +990,20 @@ Type type(Context context, boolean definition) throws ParserException {

// perform template substitution
if (context.templateMap != null) {
List<String> types = Templates.splitNamespace(type.cppName);
List<String> types = Templates.splitNamespace(type.cppName, true);
String separator = "";
type.cppName = "";
List<Type> arguments = new ArrayList<>();
for (String t : types) {
Type t2 = context.templateMap.get(t);
type.cppName += separator + (t2 != null ? t2.cppName : t);
int paramsIdx = types.size() - 1;
for (int i = 0; i < paramsIdx; i++) {
Type t2 = context.templateMap.get(types.get(i));
type.cppName += separator + (t2 != null ? t2.cppName : types.get(i));
if (t2 != null && t2.arguments != null) {
arguments.addAll(Arrays.asList(t2.arguments));
}
separator = "::";
}
type.cppName += types.get(paramsIdx);
if (arguments.size() > 0) {
type.arguments = arguments.toArray(new Type[0]);
}
Expand Down Expand Up @@ -1109,22 +1144,15 @@ Type type(Context context, boolean definition) throws ParserException {
}
}
if (context.cppName != null && type.javaName.length() > 0) {
String cppName = type.cppName;
String groupName = context.cppName;
String cppNameStripped = Templates.strip(cppName);
String groupNameStripped = Templates.strip(groupName);
if (cppNameStripped.length() == cppName.length() && groupNameStripped.length() != groupName.length()) {
groupName = groupNameStripped;
} else if (cppNameStripped.length() != cppName.length() && groupNameStripped.length() == groupName.length()) {
cppName = cppNameStripped;
}
String cppName = Templates.strip(type.cppName);
String groupName = Templates.strip(context.cppName);
List<String> cppNameSplit = Templates.splitNamespace(cppName);
List<String> groupNameSplit = Templates.splitNamespace(groupName);
if (cppNameSplit.size() == 1 && groupNameSplit.size() > 1)
groupName = groupNameSplit.get(groupNameSplit.size() - 1);
else if (cppNameSplit.size() > 1 && groupNameSplit.size() == 1)
cppName = cppNameSplit.get(cppNameSplit.size() - 1);
if (cppName.equals(groupName) || groupName.startsWith(cppName + "<")) {
if (cppName.equals(groupName)) {
type.constructor = !type.destructor && !type.operator
&& type.indirections == 0 && !type.reference && tokens.get().match('(', ':');
}
Expand Down Expand Up @@ -1347,11 +1375,8 @@ Declarator declarator(Context context, String defaultName, int infoNumber, boole
} else if (token.match(Token.OPERATOR)) {
dcl.operator = true;
if (!tokens.get(1).match(Token.IDENTIFIER) || tokens.get(1).match(Token.NEW, Token.DELETE)) {
// assume we can have any symbols until the first open parenthesis
dcl.cppName += "operator " + tokens.next();
for (token = tokens.next(); !token.match(Token.EOF, '('); token = tokens.next()) {
dcl.cppName += token;
}
tokens.next();
dcl.cppName += "operator " + operator(context);
break;
}
} else if (token.match('<')) {
Expand Down Expand Up @@ -2130,14 +2155,16 @@ Parameters parameters(Context context, int infoNumber, boolean useDefaults) thro
// perform template substitution
String cppName = token.value;
if (context.templateMap != null) {
List<String> types = Templates.splitNamespace(cppName);
List<String> types = Templates.splitNamespace(cppName, true);
String separator = "";
cppName = "";
for (String t : types) {
Type t2 = context.templateMap.get(t);
cppName += separator + (t2 != null ? t2.cppName : t);
int paramsIdx = types.size() - 1;
for (int i = 0; i < paramsIdx; i++) {
Type t2 = context.templateMap.get(types.get(i));
cppName += separator + (t2 != null ? t2.cppName : types.get(i));
separator = "::";
}
cppName += types.get(paramsIdx);
}

// try to qualify all the identifiers
Expand Down Expand Up @@ -2343,14 +2370,16 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti
}

boolean isQualified = Templates.splitNamespace(dcl.cppName).size() > 1;
if (context.namespace != null && !isQualified) {
if (context.namespace != null && !isQualified && !(type.constructor || type.destructor)) {
dcl.cppName = context.namespace + "::" + dcl.cppName;
}
Info info = null, fullInfo = null;
String fullname = dcl.cppName, fullname2 = dcl.cppName;
String templateArgs = declList.templateMap != null ? declList.templateMap.toString() : "";
String fullname = dcl.cppName + templateArgs;
String param1 = "", param2 = "";
if (dcl.parameters != null) {
fullname += "(";
fullname2 += "(";
param1 = "(";
param2 = "(";
String separator = "";
for (Declarator d : dcl.parameters.declarators) {
if (d != null) {
Expand All @@ -2376,25 +2405,42 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti
if (d.type.constPointer && !s.endsWith(" const")) {
s = s + " const";
}
fullname += separator + s;
fullname2 += separator + s2;
param1 += separator + s;
param2 += separator + s2;
separator = ", ";
}
}
info = fullInfo = infoMap.getFirst(fullname += ")", false);
param1 += ")";
param2 += ")";
fullname += param1;
info = fullInfo = infoMap.getFirst(fullname, false);
if (info == null) {
info = infoMap.getFirst(fullname2 += ")", false);
info = infoMap.getFirst(dcl.cppName + templateArgs + param2, false);
if (info == null && !templateArgs.isEmpty()) {
info = infoMap.getFirst(dcl.cppName + param1, false);
if (info == null) {
info = infoMap.getFirst(dcl.cppName + param2, false);
}
}
}
}
if (info == null) {
if (type.constructor) {
// get Info explicitly associated with all constructors
List<String> cppNameSplit = Templates.splitNamespace(dcl.cppName);
String name = Templates.strip(cppNameSplit.get(cppNameSplit.size() - 1));
info = fullInfo = infoMap.getFirst(dcl.cppName + "::" + name);
String name = constructorName(dcl.cppName);
fullname = name + templateArgs + param1;
info = fullInfo = infoMap.getFirst(fullname);
if (info == null) {
info = fullInfo = infoMap.getFirst(name + templateArgs + param2);
if (info == null) {
info = fullInfo = infoMap.getFirst(name + templateArgs);
}
}
}
// For constructor, we'd better not make this lookup, because of confusion
// with the class info. Kept for now for backwards compatibility.
if (info == null) {
info = infoMap.getFirst(dcl.cppName);
info = infoMap.getFirst(dcl.cppName + templateArgs);
}
if (!type.constructor && !type.destructor && !type.operator && (context.templateMap == null || context.templateMap.full())) {
infoMap.put(info != null ? new Info(info).cppNames(fullname).javaNames(null) : new Info(fullname));
Expand Down Expand Up @@ -2466,7 +2512,12 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti

type = functionAfter(context, decl, dcl, type);
context = new Context(context);
context.virtualize = (context.virtualize && type.virtual) || (info != null && info.virtualize);

// Virtualize the function if class is virtualized and C++ function is virtual
// or if function is explicitly virtualized with info.
// Exclude constructor case since we may have looked up the info of the class in lieu of
// the info of the constructor, and constructors cannot be virtualized.
context.virtualize = (context.virtualize && type.virtual) || (info != null && info.virtualize && !type.constructor);

List<Declarator> prevDcl = new ArrayList<Declarator>();
boolean first = true;
Expand Down Expand Up @@ -2526,8 +2577,7 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti
}

// use Java names that we may get here but that declarator() did not catch
String parameters = fullname.substring(dcl.cppName.length());
for (String name : context.qualify(dcl.cppName, parameters)) {
for (String name : context.qualify(dcl.cppName, param1)) {
if ((infoMap.getFirst(name, false)) != null) {
dcl.cppName = name;
break;
Expand All @@ -2539,8 +2589,8 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti
if (context.namespace != null && localName2.startsWith(context.namespace + "::")) {
localName2 = dcl.cppName.substring(context.namespace.length() + 2);
}
if (localName2.endsWith(parameters)) {
localName2 = localName2.substring(0, localName2.length() - parameters.length());
if (localName2.endsWith(param1)) {
localName2 = localName2.substring(0, localName2.length() - param1.length());
}
if (fullInfo != null && fullInfo.javaNames != null && fullInfo.javaNames.length > 0) {
dcl.javaName = fullInfo.javaNames[0];
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/org/bytedeco/javacpp/tools/TemplateMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
package org.bytedeco.javacpp.tools;

import java.util.LinkedHashMap;
import java.util.Map;

/**
*
Expand Down Expand Up @@ -67,4 +68,20 @@ Type get(String key) {
return value;
}
}

@Override
public String toString() {
String s = "<";
for (Map.Entry<String, Type> e : entrySet()) {
if (s.length() > 1) {
s += ",";
}
Type t = e.getValue();
s += t != null ? t.cppName : e.getKey();
}
if (s.endsWith(">")) {
s += " ";
}
return s + ">";
}
}
Loading

0 comments on commit a15c408

Please sign in to comment.