summaryrefslogtreecommitdiffstats
path: root/util/glgen/xmlspecparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'util/glgen/xmlspecparser.cpp')
-rw-r--r--util/glgen/xmlspecparser.cpp440
1 files changed, 440 insertions, 0 deletions
diff --git a/util/glgen/xmlspecparser.cpp b/util/glgen/xmlspecparser.cpp
new file mode 100644
index 0000000000..c2be6d292d
--- /dev/null
+++ b/util/glgen/xmlspecparser.cpp
@@ -0,0 +1,440 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB)
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the utilities of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "xmlspecparser.h"
+
+#include <QDebug>
+#include <QFile>
+#include <QRegularExpression>
+#include <QStringList>
+#include <QTextStream>
+#include <QXmlStreamReader>
+
+#ifdef SPECPARSER_DEBUG
+#define qXmlSpecParserDebug qDebug
+#else
+#define qXmlSpecParserDebug QT_NO_QDEBUG_MACRO
+#endif
+
+bool XmlSpecParser::parse()
+{
+ // Open up a stream on the actual OpenGL function spec file
+ QFile file(specFileName());
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning() << "Failed to open spec file:" << specFileName() << "Aborting";
+ return false;
+ }
+
+ QXmlStreamReader stream(&file);
+
+ // Extract the info that we need
+ parseFunctions(stream);
+ return true;
+}
+
+void XmlSpecParser::parseParam(QXmlStreamReader &stream, Function &func)
+{
+ Argument arg;
+ arg.type = QString();
+
+ while (!stream.isEndDocument()) {
+ stream.readNext();
+
+ if (stream.isStartElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "ptype") {
+ if (stream.readNext() == QXmlStreamReader::Characters)
+ arg.type.append(stream.text().toString());
+ }
+
+ else if (tag == "name") {
+ if (stream.readNext() == QXmlStreamReader::Characters)
+ arg.name = stream.text().toString().trimmed();
+ }
+ } else if (stream.isCharacters()) {
+ arg.type.append(stream.text().toString());
+ } else if (stream.isEndElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "param") {
+ // compatibility with old spec
+ QRegularExpression typeRegExp("(const )?(.+)(?<!\\*)((?:(?!\\*$)\\*)*)(\\*)?");
+
+ // remove extra whitespace
+ arg.type = arg.type.trimmed();
+
+ // set default
+ arg.direction = Argument::In;
+ arg.mode = Argument::Value;
+
+ QRegularExpressionMatch exp = typeRegExp.match(arg.type);
+ if (exp.hasMatch()) {
+ if (!exp.captured(4).isEmpty()) {
+ arg.mode = Argument::Reference;
+
+ if (exp.captured(1).isEmpty())
+ arg.direction = Argument::Out;
+ }
+
+ arg.type = exp.captured(2) + exp.captured(3);
+ }
+
+ break;
+ }
+ }
+ }
+
+ // remove any excess whitespace
+ arg.type = arg.type.trimmed();
+ arg.name = arg.name.trimmed();
+
+ // maybe some checks?
+ func.arguments.append(arg);
+}
+
+void XmlSpecParser::parseCommand(QXmlStreamReader &stream)
+{
+ Function func;
+
+ while (!stream.isEndDocument()) {
+ stream.readNext();
+
+ if (stream.isStartElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "proto") {
+ while (!stream.isEndDocument()) {
+ stream.readNext();
+ if (stream.isStartElement() && (stream.name().toString() == "name")) {
+ if (stream.readNext() == QXmlStreamReader::Characters)
+ func.name = stream.text().toString();
+ } else if (stream.isCharacters()) {
+ func.returnType.append(stream.text().toString());
+ } else if (stream.isEndElement() && (stream.name().toString() == "proto")) {
+ break;
+ }
+ }
+ }
+
+ if (tag == "param")
+ parseParam(stream, func);
+ }
+
+ else if (stream.isEndElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "command")
+ break;
+ }
+ }
+
+ // maybe checks?
+ func.returnType = func.returnType.trimmed();
+
+ // for compatibility with old spec
+ if (func.name.startsWith("gl"))
+ func.name.remove(0, 2);
+
+ m_functionList.insert(func.name, func);
+}
+
+void XmlSpecParser::parseCommands(QXmlStreamReader &stream)
+{
+ while (!stream.isEndDocument()) {
+ stream.readNext();
+
+ if (stream.isStartElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "command")
+ parseCommand(stream);
+ }
+
+ else if (stream.isEndElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "commands")
+ break;
+ }
+ }
+}
+
+void XmlSpecParser::parseRequire(QXmlStreamReader &stream, FunctionList &funcs)
+{
+ while (!stream.isEndDocument()) {
+ stream.readNext();
+
+ if (stream.isStartElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "command") {
+ QString func = stream.attributes().value("name").toString();
+
+ // for compatibility with old spec
+ if (func.startsWith("gl"))
+ func.remove(0, 2);
+
+ funcs.append(m_functionList[func]);
+ }
+ } else if (stream.isEndElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "require")
+ break;
+ }
+ }
+}
+
+void XmlSpecParser::parseRemoveCore(QXmlStreamReader &stream)
+{
+ while (!stream.isEndDocument()) {
+ stream.readNext();
+
+ if (stream.isStartElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "command") {
+ QString func = stream.attributes().value("name").toString();
+
+ // for compatibility with old spec
+ if (func.startsWith("gl"))
+ func.remove(0, 2);
+
+ // go through list of version and mark as incompatible
+ Q_FOREACH (const Version& v, m_versions) {
+ // Combine version and profile for this subset of functions
+ VersionProfile version;
+ version.version = v;
+ version.profile = VersionProfile::CoreProfile;
+
+ // Fetch the functions and add to collection for this class
+ Q_FOREACH (const Function& f, m_functions.values(version)) {
+ if (f.name == func) {
+ VersionProfile newVersion;
+ newVersion.version = v;
+ newVersion.profile = VersionProfile::CompatibilityProfile;
+
+ m_functions.insert(newVersion, f);
+ m_functions.remove(version, f);
+ }
+ }
+ }
+ }
+ } else if (stream.isEndElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "remove")
+ break;
+ }
+ }
+}
+
+void XmlSpecParser::parseFeature(QXmlStreamReader &stream)
+{
+ QRegularExpression versionRegExp("(\\d).(\\d)");
+ QXmlStreamAttributes attributes = stream.attributes();
+
+ QRegularExpressionMatch match = versionRegExp.match(attributes.value("number").toString());
+
+ if (!match.hasMatch()) {
+ qWarning() << "Malformed version indicator";
+ return;
+ }
+
+ if (attributes.value("api").toString() != "gl")
+ return;
+
+ int majorVersion = match.captured(1).toInt();
+ int minorVersion = match.captured(2).toInt();
+
+ Version v;
+ VersionProfile core, compat;
+
+ v.major = majorVersion;
+ v.minor = minorVersion;
+ core.version = compat.version = v;
+ core.profile = VersionProfile::CoreProfile;
+ compat.profile = VersionProfile::CompatibilityProfile;
+
+ while (!stream.isEndDocument()) {
+ stream.readNext();
+
+ if (stream.isStartElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "require") {
+ FunctionList funcs;
+
+ if (stream.attributes().value("profile").toString() == "compatibility") {
+ parseRequire(stream, funcs);
+ Q_FOREACH (const Function& f, funcs) {
+ m_functions.insert(compat, f);
+ }
+ } else {
+ parseRequire(stream, funcs);
+ Q_FOREACH (const Function& f, funcs) {
+ m_functions.insert(core, f);
+ }
+ }
+ } else if (tag == "remove") {
+ if (stream.attributes().value("profile").toString() == "core")
+ parseRemoveCore(stream);
+ }
+ } else if (stream.isEndElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "feature")
+ break;
+ }
+ }
+
+ m_versions.append(v);
+}
+
+void XmlSpecParser::parseExtension(QXmlStreamReader &stream)
+{
+ QXmlStreamAttributes attributes = stream.attributes();
+ QString name = attributes.value("name").toString();
+
+ while (!stream.isEndDocument()) {
+ stream.readNext();
+
+ if (stream.isStartElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "require") {
+ if (stream.attributes().value("profile").toString() == "compatibility") {
+ FunctionList funcs;
+ parseRequire(stream, funcs);
+
+ Q_FOREACH (const Function& f, funcs) {
+ FunctionProfile fp;
+ fp.function = f;
+ fp.profile = VersionProfile::CompatibilityProfile;
+ m_extensionFunctions.insert(name, fp);
+ }
+ } else {
+ FunctionList funcs;
+ parseRequire(stream, funcs);
+ Q_FOREACH (const Function& f, funcs) {
+ FunctionProfile fp;
+ fp.function = f;
+ fp.profile = VersionProfile::CoreProfile;
+ m_extensionFunctions.insert(name, fp);
+ }
+ }
+
+
+ }
+ } else if (stream.isEndElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "extension")
+ break;
+ }
+ }
+}
+
+void XmlSpecParser::parseFunctions(QXmlStreamReader &stream)
+{
+ while (!stream.isEndDocument()) {
+ stream.readNext();
+
+ if (stream.isStartElement()) {
+ QString tag = stream.name().toString();
+
+ if (tag == "feature") {
+ parseFeature(stream);
+ } else if (tag == "commands") {
+ parseCommands(stream);
+ } else if (tag == "extension") {
+ parseExtension(stream);
+ }
+ } else if (stream.isEndElement()) {
+ stream.readNext();
+ }
+ }
+
+ // hack - add GL_ARB_imaging to every version after 1.2 inclusive
+ Version versionThreshold;
+ versionThreshold.major = 1;
+ versionThreshold.minor = 2;
+ QList<FunctionProfile> funcs = m_extensionFunctions.values("GL_ARB_imaging");
+
+ VersionProfile vp;
+ vp.version = versionThreshold;
+
+ Q_FOREACH (const FunctionProfile& fp, funcs) {
+ vp.profile = fp.profile;
+ m_functions.insert(vp, fp.function);
+ }
+
+ // now we will prune any duplicates
+ QSet<QString> funcset;
+
+ Q_FOREACH (const Version& v, m_versions) {
+ // check compatibility first
+ VersionProfile vp;
+ vp.version = v;
+
+ vp.profile = VersionProfile::CompatibilityProfile;
+
+ Q_FOREACH (const Function& f, m_functions.values(vp)) {
+ // remove duplicate
+ if (funcset.contains(f.name))
+ m_functions.remove(vp, f);
+
+ funcset.insert(f.name);
+ }
+
+ vp.profile = VersionProfile::CoreProfile;
+
+ Q_FOREACH (const Function& f, m_functions.values(vp)) {
+
+ // remove duplicate
+ if (funcset.contains(f.name))
+ m_functions.remove(vp, f);
+
+ funcset.insert(f.name);
+ }
+ }
+}