From d6189e37ece49d2d9f78300c7c511e2da069dd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Guillemet?= Date: Sun, 24 Dec 2023 16:14:48 +0100 Subject: [PATCH] Improve parsing of function templates --- .../javacpp/tools/DeclarationList.java | 2 +- .../bytedeco/javacpp/tools/Declarator.java | 18 +++ .../org/bytedeco/javacpp/tools/InfoMap.java | 50 +++------ .../org/bytedeco/javacpp/tools/Parser.java | 104 ++++++++++++------ .../bytedeco/javacpp/tools/TemplateMap.java | 16 +++ .../org/bytedeco/javacpp/tools/Templates.java | 32 +++++- 6 files changed, 145 insertions(+), 77 deletions(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/DeclarationList.java b/src/main/java/org/bytedeco/javacpp/tools/DeclarationList.java index 701c70460..0c0774316 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/DeclarationList.java +++ b/src/main/java/org/bytedeco/javacpp/tools/DeclarationList.java @@ -70,7 +70,7 @@ public boolean add(Declaration decl, String fullName) { 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 ? dcl.constructorDeclarationName() : dcl.cppName) : type.cppName}) { if (name == null) { continue; } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Declarator.java b/src/main/java/org/bytedeco/javacpp/tools/Declarator.java index ad6f90303..fac934cb0 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Declarator.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Declarator.java @@ -22,6 +22,8 @@ package org.bytedeco.javacpp.tools; +import java.util.List; + /** * * @author Samuel Audet @@ -33,4 +35,20 @@ class Declarator { int infoNumber = 0, indices = 0, indirections = 0; boolean constPointer = false, operator = false, reference = false, rvalue = false; String cppName = "", javaName = "", signature = ""; + + /** + * Constructors have 2 kinds of fully qualified name: + * the calling name of a constructor, that is the name used when calling the constructor, e.g.: + * NS::CN(int) + * and the declaration name, used when declaring the constructor outside its class or when referencing a constructor + * with using to inherit constructors of base class. + * NS::CN::CN(int) + * 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. + */ + String constructorDeclarationName() { + List cppNameSplit = Templates.splitNamespace(this.cppName); + return this.cppName + "::" + Templates.strip(cppNameSplit.get(cppNameSplit.size() - 1)); + } } diff --git a/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java b/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java index ba6ee471e..ce913d64c 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java +++ b/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java @@ -185,6 +185,19 @@ 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 + Templates.SplitResult comps = Templates.splitNamespace(name); + int last = comps.size()-1; + String lastComp = comps.get(last); + comps.set(last, Templates.strip(lastComp)); + name = comps.get(0); + for (int i = 1; i <= last; i++) + name += "::" + comps.get(i); + if (comps.parameterList != null) + name += comps.parameterList; + if (name.isEmpty()) return name; + } boolean foundConst = false, simpleType = true; String prefix = null; Token[] tokens = new Tokenizer(name, null, 0).tokenize(); @@ -216,43 +229,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); diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index ca9c3c45b..4524eb70e 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -752,6 +752,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); } @@ -865,7 +887,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; } @@ -957,7 +980,7 @@ Type type(Context context, boolean definition) throws ParserException { // perform template substitution if (context.templateMap != null) { - List types = Templates.splitNamespace(type.cppName); + Templates.SplitResult types = Templates.splitNamespace(type.cppName); String separator = ""; type.cppName = ""; List arguments = new ArrayList<>(); @@ -969,6 +992,7 @@ Type type(Context context, boolean definition) throws ParserException { } separator = "::"; } + if (types.parameterList != null) type.cppName += types.parameterList; if (arguments.size() > 0) { type.arguments = arguments.toArray(new Type[0]); } @@ -1109,22 +1133,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 cppNameSplit = Templates.splitNamespace(cppName); List 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('(', ':'); } @@ -1347,11 +1364,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('<')) { @@ -2130,7 +2144,7 @@ Parameters parameters(Context context, int infoNumber, boolean useDefaults) thro // perform template substitution String cppName = token.value; if (context.templateMap != null) { - List types = Templates.splitNamespace(cppName); + Templates.SplitResult types = Templates.splitNamespace(cppName); String separator = ""; cppName = ""; for (String t : types) { @@ -2138,6 +2152,7 @@ Parameters parameters(Context context, int infoNumber, boolean useDefaults) thro cppName += separator + (t2 != null ? t2.cppName : t); separator = "::"; } + if (types.parameterList != null) cppName += types.parameterList; } // try to qualify all the identifiers @@ -2343,14 +2358,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) { @@ -2376,25 +2393,45 @@ 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 cppNameSplit = Templates.splitNamespace(dcl.cppName); - String name = Templates.strip(cppNameSplit.get(cppNameSplit.size() - 1)); - info = fullInfo = infoMap.getFirst(dcl.cppName + "::" + name); + String name = dcl.constructorDeclarationName(); + 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 (info == null && !templateArgs.isEmpty()) { + info = infoMap.getFirst(dcl.cppName); + } } 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)); @@ -2526,8 +2563,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; @@ -2539,8 +2575,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]; diff --git a/src/main/java/org/bytedeco/javacpp/tools/TemplateMap.java b/src/main/java/org/bytedeco/javacpp/tools/TemplateMap.java index 696a7e9b7..600e96b9a 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/TemplateMap.java +++ b/src/main/java/org/bytedeco/javacpp/tools/TemplateMap.java @@ -23,6 +23,7 @@ package org.bytedeco.javacpp.tools; import java.util.LinkedHashMap; +import java.util.Map; /** * @@ -67,4 +68,19 @@ Type get(String key) { return value; } } + + @Override + public String toString() { + String res = "<"; + for (Map.Entry e: entrySet()) { + if (res.length() > 1) res += ","; + Type t = e.getValue(); + if (t == null) + res += e.getKey(); + else + res += t.cppName; + } + if (res.charAt(res.length()-1) == '>') res += " "; + return res + ">"; + } } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Templates.java b/src/main/java/org/bytedeco/javacpp/tools/Templates.java index ebd2f8b89..9967345bf 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Templates.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Templates.java @@ -47,8 +47,12 @@ static boolean notExists(String s) { return strip(s).length() == s.length(); } - /** Split s at ::, but taking care of qualified template arguments */ - static List splitNamespace(String s) { + static class SplitResult extends ArrayList { + String parameterList; + } + + /** Split s at ::, but taking care of qualified template arguments and qualified function parameters, if any. */ + static SplitResult splitNamespace(String s) { String sTemplatesMasked = s; for (;;) { Matcher m = templatePattern.matcher(sTemplatesMasked); @@ -60,18 +64,36 @@ static List splitNamespace(String s) { break; } } - ArrayList comps = new ArrayList<>(); + SplitResult comps = new SplitResult(); + int pIndex = sTemplatesMasked.lastIndexOf(')'); // last because of function pointer types like void(*)() + if (pIndex > 0) { + // Pointer list may contain function pointer types with parentheses + int count = 1; + for (pIndex--; pIndex >= 0; pIndex--) { + char c = sTemplatesMasked.charAt(pIndex); + if (c == ')') count++; + else if (c == '(') { + count--; + if (count == 0) break; + } + } + } int start = 0; for (;;) { int i = sTemplatesMasked.indexOf("::", start); - if (i >= 0) { + if (i >= 0 && (i < pIndex || pIndex == -1)) { comps.add(s.substring(start, i)); start = i + 2; } else { break; } } - comps.add(s.substring(start)); + if (pIndex >= 0) { + comps.add(s.substring(start, pIndex)); + comps.parameterList = s.substring(pIndex); + } else { + comps.add(s.substring(start)); + } return comps; } }